From 9bfdded074f9597f5724fb0a401dab7644aad958 Mon Sep 17 00:00:00 2001 From: RomainROCH <153511345+RomainROCH@users.noreply.github.com> Date: Tue, 29 Sep 2026 05:07:28 +0200 Subject: [PATCH 01/15] docs: preserve resource benchmark evidence --- README.md | 22 + .../2026-09-29-v0.2.0/.gitattributes | 2 + .../2026-09-29-v0.2.0/Measure-Wardoff.ps1 | 97 ++ docs/benchmarks/2026-09-29-v0.2.0/RAPPORT.md | 108 ++ docs/benchmarks/2026-09-29-v0.2.0/README.md | 94 ++ .../2026-09-29-v0.2.0/SHA256SUMS.txt | 36 + docs/benchmarks/2026-09-29-v0.2.0/analyze.py | 147 +++ docs/benchmarks/2026-09-29-v0.2.0/binary.json | 13 + .../2026-09-29-v0.2.0/provenance.json | 19 + .../2026-09-29-v0.2.0/results/metadata.json | 32 + .../2026-09-29-v0.2.0/results/progress.log | 18 + .../results/run-1-allow-end.json | 12 + .../results/run-1-allow-start.json | 12 + .../2026-09-29-v0.2.0/results/run-1-allow.csv | 301 +++++ .../results/run-1-baseline.csv | 301 +++++ .../results/run-1-block-end.json | 12 + .../results/run-1-block-start.json | 12 + .../2026-09-29-v0.2.0/results/run-1-block.csv | 301 +++++ .../results/run-2-allow-end.json | 12 + .../results/run-2-allow-start.json | 12 + .../2026-09-29-v0.2.0/results/run-2-allow.csv | 301 +++++ .../results/run-2-baseline.csv | 301 +++++ .../results/run-2-block-end.json | 12 + .../results/run-2-block-start.json | 12 + .../2026-09-29-v0.2.0/results/run-2-block.csv | 301 +++++ .../results/run-3-allow-end.json | 12 + .../results/run-3-allow-start.json | 12 + .../2026-09-29-v0.2.0/results/run-3-allow.csv | 301 +++++ .../results/run-3-baseline.csv | 301 +++++ .../results/run-3-block-end.json | 12 + .../results/run-3-block-start.json | 12 + .../2026-09-29-v0.2.0/results/run-3-block.csv | 301 +++++ .../2026-09-29-v0.2.0/results/status.txt | 1 + .../results/update-layer-evidence.jsonl | 3 + docs/benchmarks/2026-09-29-v0.2.0/summary.csv | 93 ++ .../benchmarks/2026-09-29-v0.2.0/summary.json | 1014 +++++++++++++++++ .../2026-09-29-v0.2.0/validation.json | 78 ++ docs/benchmarks/2026-09-29-v0.2.0/verify.py | 33 + 38 files changed, 4663 insertions(+) create mode 100644 docs/benchmarks/2026-09-29-v0.2.0/.gitattributes create mode 100644 docs/benchmarks/2026-09-29-v0.2.0/Measure-Wardoff.ps1 create mode 100644 docs/benchmarks/2026-09-29-v0.2.0/RAPPORT.md create mode 100644 docs/benchmarks/2026-09-29-v0.2.0/README.md create mode 100644 docs/benchmarks/2026-09-29-v0.2.0/SHA256SUMS.txt create mode 100644 docs/benchmarks/2026-09-29-v0.2.0/analyze.py create mode 100644 docs/benchmarks/2026-09-29-v0.2.0/binary.json create mode 100644 docs/benchmarks/2026-09-29-v0.2.0/provenance.json create mode 100644 docs/benchmarks/2026-09-29-v0.2.0/results/metadata.json create mode 100644 docs/benchmarks/2026-09-29-v0.2.0/results/progress.log create mode 100644 docs/benchmarks/2026-09-29-v0.2.0/results/run-1-allow-end.json create mode 100644 docs/benchmarks/2026-09-29-v0.2.0/results/run-1-allow-start.json create mode 100644 docs/benchmarks/2026-09-29-v0.2.0/results/run-1-allow.csv create mode 100644 docs/benchmarks/2026-09-29-v0.2.0/results/run-1-baseline.csv create mode 100644 docs/benchmarks/2026-09-29-v0.2.0/results/run-1-block-end.json create mode 100644 docs/benchmarks/2026-09-29-v0.2.0/results/run-1-block-start.json create mode 100644 docs/benchmarks/2026-09-29-v0.2.0/results/run-1-block.csv create mode 100644 docs/benchmarks/2026-09-29-v0.2.0/results/run-2-allow-end.json create mode 100644 docs/benchmarks/2026-09-29-v0.2.0/results/run-2-allow-start.json create mode 100644 docs/benchmarks/2026-09-29-v0.2.0/results/run-2-allow.csv create mode 100644 docs/benchmarks/2026-09-29-v0.2.0/results/run-2-baseline.csv create mode 100644 docs/benchmarks/2026-09-29-v0.2.0/results/run-2-block-end.json create mode 100644 docs/benchmarks/2026-09-29-v0.2.0/results/run-2-block-start.json create mode 100644 docs/benchmarks/2026-09-29-v0.2.0/results/run-2-block.csv create mode 100644 docs/benchmarks/2026-09-29-v0.2.0/results/run-3-allow-end.json create mode 100644 docs/benchmarks/2026-09-29-v0.2.0/results/run-3-allow-start.json create mode 100644 docs/benchmarks/2026-09-29-v0.2.0/results/run-3-allow.csv create mode 100644 docs/benchmarks/2026-09-29-v0.2.0/results/run-3-baseline.csv create mode 100644 docs/benchmarks/2026-09-29-v0.2.0/results/run-3-block-end.json create mode 100644 docs/benchmarks/2026-09-29-v0.2.0/results/run-3-block-start.json create mode 100644 docs/benchmarks/2026-09-29-v0.2.0/results/run-3-block.csv create mode 100644 docs/benchmarks/2026-09-29-v0.2.0/results/status.txt create mode 100644 docs/benchmarks/2026-09-29-v0.2.0/results/update-layer-evidence.jsonl create mode 100644 docs/benchmarks/2026-09-29-v0.2.0/summary.csv create mode 100644 docs/benchmarks/2026-09-29-v0.2.0/summary.json create mode 100644 docs/benchmarks/2026-09-29-v0.2.0/validation.json create mode 100644 docs/benchmarks/2026-09-29-v0.2.0/verify.py diff --git a/README.md b/README.md index 8149682..70fde46 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,28 @@ Wardoff is a Windows-native Rust utility for people who want a visible, scriptable way to keep a machine in a protected **Block** state during gaming sessions, overnight jobs, remote work, or maintenance windows. The project is intentionally honest about what the current MVP does today and what still belongs to later releases. +## Measured resource usage + +Wardoff is lightweight while protecting a machine in the background. In our +v0.2.0 benchmark on an Intel Core i7-6700HQ running Windows 11 IoT Enterprise +LTSC, Block mode averaged approximately **14 MiB of resident memory** and +**less than 0.01% total-machine CPU** across three five-minute background runs. +CPU is normalized across all eight logical processors. + +| Mean process resource usage | Allow | Block | +|---|---:|---:| +| Resident memory / Working Set (MiB) | 12.57 | 13.95 | +| Private committed memory (MiB) | 1.96 | 3.25 | +| CPU (% of total machine capacity) | 0.0024% | 0.0050% | + +The [dated benchmark evidence](docs/benchmarks/2026-09-29-v0.2.0/README.md) +includes the protocol, 2,700 raw samples, system information, tested binary +hash, per-run statistics, limitations, and an integrity/reproduction checker. +These are measurements on **one machine**, not universal guarantees. The +UpdateOrchestrator Reboot task was absent, so its protection layer was inactive. +Actual shutdown blocking, startup cost, heavy command workloads, battery use, +and long-term memory growth were not tested. + ## Current MVP status Wardoff currently ships a working tray/runtime app plus CLI with these MVP-level capabilities: diff --git a/docs/benchmarks/2026-09-29-v0.2.0/.gitattributes b/docs/benchmarks/2026-09-29-v0.2.0/.gitattributes new file mode 100644 index 0000000..c4b3d3a --- /dev/null +++ b/docs/benchmarks/2026-09-29-v0.2.0/.gitattributes @@ -0,0 +1,2 @@ +# Preserve evidence bytes and checksums on every checkout, including Windows. +* -text whitespace=blank-at-eol,blank-at-eof,space-before-tab,cr-at-eol diff --git a/docs/benchmarks/2026-09-29-v0.2.0/Measure-Wardoff.ps1 b/docs/benchmarks/2026-09-29-v0.2.0/Measure-Wardoff.ps1 new file mode 100644 index 0000000..4d333c7 --- /dev/null +++ b/docs/benchmarks/2026-09-29-v0.2.0/Measure-Wardoff.ps1 @@ -0,0 +1,97 @@ +param([int]$Seconds=300,[int]$Warmup=30,[int]$Repeats=3,[string]$OutputName='results') +$ErrorActionPreference='Stop' +[Threading.Thread]::CurrentThread.CurrentCulture=[Globalization.CultureInfo]::InvariantCulture +$out=Join-Path $PSScriptRoot $OutputName +New-Item -ItemType Directory -Force $out | Out-Null +$exe=Join-Path $PSScriptRoot 'wardoff.exe' +$expected='96D1700C492B1689E6CB3A18847481EB9A203E30DB4D4BA273D4B458ED6A84BC' +if((Get-FileHash $exe).Hash -ne $expected){throw 'Executable hash mismatch'} +if(Get-Process wardoff -ErrorAction SilentlyContinue){throw 'Existing Wardoff process: preserve it and stop.'} +Add-Type @' +using System; +using System.Runtime.InteropServices; +public static class BenchNative { + [DllImport("kernel32.dll",SetLastError=true)] public static extern bool GetSystemTimes(out ulong idle,out ulong kernel,out ulong user); + [DllImport("user32.dll",SetLastError=true)] public static extern bool PostThreadMessage(uint thread,uint msg,UIntPtr w,IntPtr l); + [DllImport("kernel32.dll",SetLastError=true)] public static extern bool GetProcessIoCounters(IntPtr h,out IO v); + [DllImport("kernel32.dll")] public static extern uint SetThreadExecutionState(uint flags); + [StructLayout(LayoutKind.Sequential)] public struct IO {public ulong ReadOps,WriteOps,OtherOps,ReadBytes,WriteBytes,OtherBytes;} + public static ulong[] Times(){ulong i,k,u;if(!GetSystemTimes(out i,out k,out u))throw new Exception("GetSystemTimes failed");return new ulong[]{i,k,u};} + public static IO Counters(IntPtr h){IO v;if(!GetProcessIoCounters(h,out v))throw new Exception("GetProcessIoCounters failed");return v;} +} +'@ +function Save-Json($value,$path){$value | ConvertTo-Json -Depth 10 | Set-Content -Encoding UTF8 $path} +function Status { $text= & $exe --status; if($LASTEXITCODE -ne 0){throw 'Status failed'}; return ($text | ConvertFrom-Json) } +function Stop-Owned($p){ + if($null -eq $p){return}; $p.Refresh(); if($p.HasExited){throw 'Runtime exited unexpectedly'} + $main=$p.Threads | Sort-Object StartTime | Select-Object -First 1 + if(-not [BenchNative]::PostThreadMessage([uint32]$main.Id,0x12,[UIntPtr]::Zero,[IntPtr]::Zero)){throw 'WM_QUIT failed'} + if(-not $p.WaitForExit(20000)){throw 'Graceful exit timed out; process preserved'} + if(Get-Process wardoff -ErrorAction SilentlyContinue){throw 'Unexpected surviving Wardoff process'} +} +$owned=$null +try { + $admin=[Security.Principal.WindowsPrincipal]::new([Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) + $cpu=Get-CimInstance Win32_Processor + $os=Get-CimInstance Win32_OperatingSystem + $logical=($cpu | Measure-Object NumberOfLogicalProcessors -Sum).Sum + $meta=[ordered]@{startedUtc=[DateTime]::UtcNow.ToString('o');seconds=$Seconds;warmupSeconds=$Warmup;repeats=$Repeats;admin=$admin;cpu=@($cpu | Select-Object Name,NumberOfCores,NumberOfLogicalProcessors);os=($os | Select-Object Caption,Version,BuildNumber,TotalVisibleMemorySize);logicalProcessors=$logical;powerPlan=(powercfg /getactivescheme | Out-String).Trim();binarySha256=$expected;version='0.2.0';releaseTag='v0.2.0';releaseCommit='9f6c0375c851d0a5b933da0bd69bafc75b215cf5';mainCommit='aa40e8982273fa66b86b806ec1424a547affdbd3';cpuDefinition='100 * process CPU seconds / actual elapsed seconds / logical processors';ramUnit='MiB = 1048576 bytes';ioDefinition='Process I/O includes file/device/network operations, not physical disk traffic';p95Definition='nearest rank';collectorPid=$PID} + Save-Json $meta (Join-Path $out 'metadata.json') + # Keep system awake equally in every condition; no change to display or power plan. + [void][BenchNative]::SetThreadExecutionState([uint32]2147483649) + $orders=@(@('baseline','allow','block'),@('allow','block','baseline'),@('block','baseline','allow')) + $collector=Get-Process -Id $PID + for($r=1;$r -le $Repeats;$r++){ + foreach($scenario in $orders[($r-1)%3]){ + $label="run-$r-$scenario" + "$(Get-Date -Format o) START $label" | Add-Content (Join-Path $out 'progress.log') + if($scenario -ne 'baseline'){ + Start-Process -FilePath $exe -ArgumentList '--allow' -WindowStyle Hidden + Start-Sleep -Seconds 3 + $ps=@(Get-Process wardoff -ErrorAction SilentlyContinue) + if($ps.Count -ne 1){throw 'Expected exactly one runtime'} + $owned=$ps[0] + if($owned.Path -ne $exe){throw 'Unexpected executable path'} + if($scenario -eq 'block'){ & $exe --block | Out-Null; if($LASTEXITCODE -ne 0){throw 'Block command failed'} } + } + Start-Sleep -Seconds $Warmup + if($owned){$s=Status; if($s.state -ne $scenario){throw 'Wrong runtime state'}; Save-Json $s (Join-Path $out "$label-start.json")} + elseif(Get-Process wardoff -ErrorAction SilentlyContinue){throw 'Baseline contaminated'} + $rows=[Collections.Generic.List[object]]::new() + $watch=[Diagnostics.Stopwatch]::StartNew() + $lastT=$watch.Elapsed.TotalSeconds; $lastSys=[BenchNative]::Times() + $collector.Refresh(); $lastCollector=$collector.TotalProcessorTime.TotalSeconds + $lastCpu=0.; $lastIo=$null + if($owned){$owned.Refresh();$lastCpu=$owned.TotalProcessorTime.TotalSeconds;$lastIo=[BenchNative]::Counters($owned.Handle)} + for($i=1;$i -le $Seconds;$i++){ + $wait=1000*($i-$watch.Elapsed.TotalSeconds); if($wait -gt 0){Start-Sleep -Milliseconds ([int]$wait)} + $now=$watch.Elapsed.TotalSeconds;$dt=$now-$lastT;$sys=[BenchNative]::Times() + $total=($sys[1]-$lastSys[1])+($sys[2]-$lastSys[2]);$idle=$sys[0]-$lastSys[0] + $collector.Refresh();$cc=$collector.TotalProcessorTime.TotalSeconds + $row=[ordered]@{run=$r;scenario=$scenario;sample=$i;utc=[DateTime]::UtcNow.ToString('o');elapsed_s=$now;interval_s=$dt;system_cpu_pct=100*($total-$idle)/$total;collector_cpu_pct=100*($cc-$lastCollector)/$dt/$logical;pid='';process_cpu_s='';cpu_pct='';private_mib='';working_set_mib='';threads='';handles='';io_read_bytes_s='';io_write_bytes_s=''} + if($owned){ + $owned.Refresh();if($owned.HasExited){throw 'Runtime died during measurement'} + $pc=$owned.TotalProcessorTime.TotalSeconds;$io=[BenchNative]::Counters($owned.Handle) + $row.pid=$owned.Id;$row.process_cpu_s=$pc-$lastCpu;$row.cpu_pct=100*($pc-$lastCpu)/$dt/$logical + $row.private_mib=$owned.PrivateMemorySize64/1MB;$row.working_set_mib=$owned.WorkingSet64/1MB + $row.threads=$owned.Threads.Count;$row.handles=$owned.HandleCount + $row.io_read_bytes_s=($io.ReadBytes-$lastIo.ReadBytes)/$dt;$row.io_write_bytes_s=($io.WriteBytes-$lastIo.WriteBytes)/$dt + $lastCpu=$pc;$lastIo=$io + } + $rows.Add([pscustomobject]$row);$lastT=$now;$lastSys=$sys;$lastCollector=$cc + } + $rows | Export-Csv -NoTypeInformation -Encoding UTF8 (Join-Path $out "$label.csv") + if($owned){$s=Status;Save-Json $s (Join-Path $out "$label-end.json");if($s.state -ne $scenario){throw 'State changed'};Stop-Owned $owned;$owned=$null} + elseif(Get-Process wardoff -ErrorAction SilentlyContinue){throw 'Baseline contaminated'} + "$(Get-Date -Format o) COMPLETE $label samples=$($rows.Count)" | Add-Content (Join-Path $out 'progress.log') + } + } + 'SUCCESS' | Set-Content (Join-Path $out 'status.txt') +} catch { + $_ | Out-String | Set-Content (Join-Path $out 'error.txt') + 'FAILED' | Set-Content (Join-Path $out 'status.txt') + throw +} finally { + if($owned){Stop-Owned $owned} + [void][BenchNative]::SetThreadExecutionState([uint32]2147483648) +} diff --git a/docs/benchmarks/2026-09-29-v0.2.0/RAPPORT.md b/docs/benchmarks/2026-09-29-v0.2.0/RAPPORT.md new file mode 100644 index 0000000..b6b39bb --- /dev/null +++ b/docs/benchmarks/2026-09-29-v0.2.0/RAPPORT.md @@ -0,0 +1,108 @@ +# Benchmark Wardoff 0.2.0 + + +## Comparaison des moyennes + +| Mesure | Allow | Block | +|---|---:|---:| +| Mémoire résidente (MiB) | 12.57 | 13.95 | +| Mémoire privée (MiB) | 1.96 | 3.25 | +| CPU (% du total machine) | 0.0024 | 0.0050 | + +**Allow** : protections désactivées. **Block** : protections actives en arrière-plan. Les valeurs CPU sont des pourcentages de la capacité totale de la machine. + +Mesures locales commencées le 2026-09-29 (UTC). Binaire officiel de la dernière release disponible au moment de cette campagne, vérifié avant exécution. + +## Résultats + +| Scénario | Mesure | Moyenne | Médiane | P95 | Maximum | +|---|---|---:|---:|---:|---:| +| Allow | Mémoire privée (MiB) | 1.955 | 1.957 | 2.020 | 2.043 | +| Allow | Working Set (MiB) | 12.568 | 12.562 | 12.715 | 12.754 | +| Allow | CPU Wardoff (% du total machine) | 0.00239 | 0.00000 | 0.00000 | 0.19715 | +| Allow | Threads | 5.986 | 5.000 | 8.000 | 8.000 | +| Block | Mémoire privée (MiB) | 3.251 | 3.246 | 3.336 | 3.359 | +| Block | Working Set (MiB) | 13.946 | 14.047 | 14.266 | 14.277 | +| Block | CPU Wardoff (% du total machine) | 0.00499 | 0.00000 | 0.00000 | 0.19705 | +| Block | Threads | 10.333 | 10.000 | 13.000 | 13.000 | + +Les moyennes CPU sont pondérées par la durée réelle des intervalles. Médiane, P95 et maximum décrivent les échantillons d’environ une seconde. Un maximum ici ne représente pas un pic instantané. + +## Répétitions et activité ambiante + +| Répétition | Scénario | CPU Wardoff moyen (%) | Working Set moyen (MiB) | CPU système moyen (%) | +|---|---|---:|---:|---:| +| 1 | baseline | — | — | 9.427 | +| 1 | allow | 0.00260 | 12.527 | 9.332 | +| 1 | block | 0.00391 | 14.069 | 8.863 | +| 2 | baseline | — | — | 9.093 | +| 2 | allow | 0.00326 | 12.607 | 9.528 | +| 2 | block | 0.00846 | 13.670 | 9.669 | +| 3 | baseline | — | — | 9.412 | +| 3 | allow | 0.00130 | 12.568 | 8.561 | +| 3 | block | 0.00260 | 14.100 | 8.549 | + +La baseline sert à documenter l’activité ambiante. Elle n’est pas soustraite aux mesures du processus. Le CPU système inclut toutes les applications, dont le collecteur et l’environnement de travail ; il ne mesure pas le coût causal de Wardoff. + +## Protocole + +- 3 répétitions × 3 scénarios × 300 secondes : 45 minutes mesurées, 900 échantillons par scénario. +- Baseline : aucun processus Wardoff. Allow : icône visible, protections désactivées. Block : même interface visible, protections en fonctionnement normal, sans tentative réelle d’arrêt. +- Ordres : baseline/allow/block, allow/block/baseline, block/baseline/allow. Nouveau processus pour chaque phase avec Wardoff ; stabilisation de 30 secondes exclue avant chaque mesure. +- Échantillonnage sur horloge monotone, cible d’une seconde. CPU = différence de temps processeur / temps réel / 8 processeurs logiques × 100. 100 % correspond à toute la machine ; multiplier par 8 pour la convention où un cœur logique vaut 100 %. +- Mémoire privée : PrivateMemorySize64, engagement privé du processus, pas nécessairement résident. Working Set : mémoire résidente du processus, pages partagées incluses. Ce ne sont pas deux mesures à additionner. 1 MiB = 1 048 576 octets. +- P95 : rang le plus proche par excès (ceil(0,95 × N)). Statistiques descriptives ; les secondes consécutives ne sont pas des répétitions indépendantes. +- État et couches contrôlés hors mesure au début et à la fin ; sortie normale via WM_QUIT, avec attente de fermeture. Aucun arrêt Windows, redémarrage, suspension, changement de démarrage automatique ou fermeture d’applications tierces. +- Le collecteur empêche la veille système de manière identique dans tous les scénarios ; le plan d’alimentation reste inchangé. Données conservées en mémoire pendant chaque phase, exportées entre phases. + +## Machine et provenance + +- CPU : Intel(R) Core(TM) i7-6700HQ CPU @ 2.60GHz, 4 cœurs / 8 processeurs logiques. +- RAM visible par Windows : 15.867 GiB (machine équipée de 16 Go). +- Système : Microsoft Windows 11 IoT Enterprise LTSC, 10.0.26100, build 26100.9445 (révision vérifiée après mesure). +- Alimentation : Power Scheme GUID: 381b4222-f694-41f0-9685-ff5bb260df2e (Balanced). Exécution administrateur : True. +- Release : [v0.2.0](https://github.com/RomainROCH/Wardoff/releases/tag/v0.2.0). Version affichée par le binaire : wardoff 0.2.0. +- SHA-256 du binaire : `96d1700c492b1689e6cb3a18847481eb9a203e30db4d4ba273d4b458ed6a84bc` ; correspond au digest de l’asset GitHub. +- Commit du tag : `9f6c0375c851d0a5b933da0bd69bafc75b215cf5`. HEAD local/main distant vérifié : `aa40e8982273fa66b86b806ec1424a547affdbd3`. +- Après récupération des références distantes, aucune différence de code applicatif, manifeste, script de build ou dépendances entre le tag, main et dev. Les différences concernent la documentation et les consignes. +- Tentative de reconstruction avec cargo build --release --locked refusée par Cargo car le verrou des dépendances nécessite une mise à jour. Les mesures concernent donc explicitement le binaire distribué, pas une reconstruction locale. Le rattachement à la release et son digest sont vérifiés ; aucune reproductibilité binaire n’est revendiquée. + +## Couches réellement actives + +- `shutdown` : active. +- `local_shutdown` : active. +- `update` : inactive. +- `remote` : active. +- `sleep` : active. + +Le journal du runtime administrateur confirme que la tâche `Microsoft\Windows\UpdateOrchestrator\Reboot` est absente sur cette machine LTSC. La couche Update est donc ignorée normalement. Ces résultats couvrent seulement cette combinaison effective de couches. La surveillance locale signalée active par le runtime ne constitue pas une preuve d’efficacité contre un arrêt forcé. + +## Contrôles et limites + +- Validation : 2700 échantillons, 9 phases complètes, état et identité du processus vérifiés. +- Intervalles > 1,25 s : 0 ; intervalle maximal : 1.025 s. +- Le CPU du collecteur figure dans summary.csv et dans les données brutes. Il reste inclus dans le CPU système. +- CPU système pendant les baselines : moyenne 9.31 %, P95 17.12 %, maximum 86.92 %. Le bureau n’était donc pas au repos absolu. Aucun échantillon n’a été supprimé. +- CPU moyen du collecteur : baseline 0.014 %, Allow 0.049 %, Block 0.040 % du total machine. Son coût reste distinct du processus Wardoff. +- Les moyennes CPU et Working Set ont été recalculées indépendamment en PowerShell à partir des CSV ; elles concordent avec les résultats Python. +- Le processus CPU peut être quantifié par le compteur Windows : un échantillon à zéro ne prouve pas une absence absolue de travail. La moyenne intégrée sur 15 minutes par mode est plus informative. +- Mesure sur une machine utilisée normalement, avec ses services et applications ouverts, pas sur une image Windows isolée. Pas de contrôle des températures, fréquences instantanées ou activité utilisateur. Ne pas attribuer une différence de CPU système à Wardoff. +- Démarrage exclu ; pas de test de charge de commandes, de vrai blocage d’arrêt, de batterie ou de fuite mémoire sur plusieurs heures. Résultats non généralisables à toutes les machines ou toutes les protections. +- Les compteurs I/O sont ceux du processus (fichiers, périphériques, réseau), pas une mesure du trafic physique du disque. + +## Fichiers et reproduction + +- `Measure-Wardoff.ps1` : collecteur autonome PowerShell ; lancer en administrateur avec le binaire vérifié dans le même dossier. +- `results/` : CSV bruts, états JSON, métadonnées, journal des phases et statut final. +- `analyze.py` : validation et calcul des statistiques avec Python standard, sans dépendances supplémentaires. +- `summary.csv`, `summary.json`, `validation.json` : résultats et contrôles reproductibles. +- Le pilote technique court est exclu des statistiques et de ce dossier de preuves ; il reste dans l’archive locale initiale. +- L’empreinte du collecteur n’a pas été enregistrée au démarrage de la campagne. Le script conservé est identique à la copie de l’archive initiale ; le manifeste ultérieur protège cette copie sans constituer une attestation cryptographique de son exécution. + +Ce dossier conserve les preuves de la campagne. Son état de sauvegarde distante doit être vérifié par la référence Git distante ; la présence de ces fichiers ne constitue pas à elle seule une sauvegarde hors machine. + +## Formulation utilisable avec son contexte + +> On an Intel Core i7-6700HQ running Windows 11 IoT Enterprise LTSC, Wardoff 0.2.0 averaged approximately 14 MiB resident memory and less than 0.01% total-system CPU in Block mode across three five-minute background runs. UpdateOrchestrator task protection was unavailable because the task was absent. + +Cette formulation décrit une mesure du processus sur cette machine ; elle ne promet ni la même consommation partout, ni une protection contre tous les arrêts Windows. diff --git a/docs/benchmarks/2026-09-29-v0.2.0/README.md b/docs/benchmarks/2026-09-29-v0.2.0/README.md new file mode 100644 index 0000000..254d218 --- /dev/null +++ b/docs/benchmarks/2026-09-29-v0.2.0/README.md @@ -0,0 +1,94 @@ +# Wardoff v0.2.0 resource benchmark — 2026-09-29 + +This directory preserves the report, protocol, retained measurement script, raw +samples, executable identity, system information, and analysis for this campaign. +Use this dated evidence when citing resource usage; do not generalize it to other +versions, hardware, or protection configurations. + +## Mean process resource usage + +| Metric | Allow | Block | +|---|---:|---:| +| Resident memory / Working Set (MiB) | 12.57 | 13.95 | +| Private committed memory (MiB) | 1.96 | 3.25 | +| CPU (% of total machine capacity) | 0.0024% | 0.0050% | + +**Allow:** application running with protection disabled. **Block:** normal +background protection enabled. Both use the visible tray interface. Private +committed memory and Working Set are different measures and must not be added. + +The machine was an Intel Core i7-6700HQ (4 cores / 8 logical processors), 16 GiB +installed RAM, Windows 11 IoT Enterprise LTSC build 26100.9445, Balanced power +plan. Wardoff ran elevated. The UpdateOrchestrator Reboot task was absent, so its +protection layer was inactive. The other four layers reported active in Block. +No real shutdown attempt was part of the workload. + +## Preserved evidence + +- [Detailed report and protocol (French)](RAPPORT.md): means, medians, nearest-rank + P95, maxima, individual repetitions, sampling method, and limitations. +- [Retained collection script](Measure-Wardoff.ps1): one-second target intervals, + monotonic elapsed time, process and system CPU, private memory, Working Set, + threads, handles and process I/O. Its bytes match the original local archive. +- [Raw data](results/): nine CSV files containing 2,700 samples; runtime state + snapshots, phase log, completion status and Update-layer evidence. +- [System and measurement metadata](results/metadata.json) and + [source provenance](provenance.json). +- [Binary identity](binary.json): version, release URL, source tag and SHA-256 + verified against the downloaded executable and GitHub release asset digest. +- [Analysis script](analyze.py), [statistics](summary.csv), and + [validation results](validation.json). +- [File integrity manifest](SHA256SUMS.txt): SHA-256 of every other file in this + directory. The scoped `.gitattributes` preserves their bytes across platforms. + +The executable is not duplicated in Git. Retrieve it from the URL in +`binary.json` and require the recorded SHA-256 before using it. The preliminary +pilot, redundant console output, and full GitHub API response are not part of +this evidence set. All final-run CSV and state files are preserved unchanged. + +No collector-script checksum was recorded at run startup. The archived copy +matches the initial local archive, and the later manifest protects its bytes; +this is not cryptographic attestation that those bytes executed. A future +collector should record its own digest at startup. This historical collector +and the original run metadata have not been retroactively modified to claim it. + +## Audit without rerunning the experiment + +From this directory, use Python 3 (standard library only): + +```powershell +python verify.py +``` + +The verifier checks manifest coverage and every file digest, then reruns the +analysis in a temporary directory and compares the generated statistics, +validation and detailed report. It does not launch Wardoff or alter the stored +evidence. Checks require nine phases of 300 samples, each phase within 1.25 +seconds of its target duration, no sample interval above 1.25 seconds, consistent +elapsed-time totals, and consistent runtime states. These tolerances are for +this recorded campaign, not a universal acceptance rule for future experiments. + +To conduct a **new** experiment, copy `Measure-Wardoff.ps1` and the verified +executable into a separate new directory, then run the script in an elevated +PowerShell session. Do not overwrite this dated evidence set. The collector is +an exact historical script; its version/hash and source metadata are pinned to +this campaign and must be reviewed for a different build. + +## Interpretation and retention + +There were three 300-second repetitions per scenario, with 30 seconds of +excluded warmup and rotated scenario order: 45 minutes measured in total. +CPU is normalized across all eight logical processors and uses actual elapsed +time. A zero median/P95 reflects timer quantization and does not mean zero CPU +work. Maxima represent approximately one-second intervals, not instantaneous +peaks. The desktop was not isolated; baseline system CPU averaged 9.31%. + +The figures describe Wardoff's process, not the complete causal cost of Windows +services or kernel activity. Startup, actual shutdown blocking, sustained +foreground workloads, battery use, and long-term leaks were not measured. + +Keep the whole directory together. Corrections to the report or analysis must +be explicit Git changes followed by manifest regeneration; never silently edit +raw measurements. A future campaign belongs in a new dated directory. Local +files, a ZIP, and a local commit alone are not an off-machine backup: completion +requires the evidence commit on an authorized remote, with its SHA read back. diff --git a/docs/benchmarks/2026-09-29-v0.2.0/SHA256SUMS.txt b/docs/benchmarks/2026-09-29-v0.2.0/SHA256SUMS.txt new file mode 100644 index 0000000..9a168fa --- /dev/null +++ b/docs/benchmarks/2026-09-29-v0.2.0/SHA256SUMS.txt @@ -0,0 +1,36 @@ +f9161820b4789e216033dc3e0860bf6bd027e8e9336f834acd2927615efd5a8f .gitattributes +f1b00c24bd5d40dd334c12f641a45f122853ca58dd0adac9063c4c79bfe84ca0 analyze.py +ccd539b23c4b9d597d9c1b7f0fcc74a4b569f5a76e9846c855fb5daad94d3886 binary.json +c19f1edb4cb3c6523b9e4c96a4a1391e901e94e507b871ce1f519cbdab989ff9 Measure-Wardoff.ps1 +d31f75278c92b7fff43aa975094dc56c923fb8d190bc9c1451be7544da897911 provenance.json +9beaa33ffa568ba8af79996cfe26a8d8beac9c13dccf5e8ef2b569d977228f51 RAPPORT.md +44a46bb90fbfcd5e3b9f2eadbc5d6bf55dd6d81925a675c1a5ac58962776de9d README.md +d01b00e39e2a123f56e2248e5d244f4ab5a3c15d5b07e412d3153f35d7e0f756 results/metadata.json +fca1b4a18d3bf2621765981169f1b363959ded313ce9fa2087f8a8abb11f74b5 results/progress.log +9b59ba196116e27d5de0a83bb1b06cf5acaec4930babe0ead12389fedca4f6b3 results/run-1-allow-end.json +db9298d58b5fdf3c082422ae729b7d05b0d4719f5f3392072e5bb61fd3aa4466 results/run-1-allow-start.json +95615320b11fdd267d9c04907aa4d24b28420bdc9f19d9d6fb34616c0310781d results/run-1-allow.csv +b84725b414bdf183e96e8587854fcbff3e500b5f9ffc949f63d9cce01e27e319 results/run-1-baseline.csv +337329cbd5eb256bc3755bed597be49e6e9948ae445dea55c8cd65dcb6bae3ef results/run-1-block-end.json +5f2095d4586cc5fb1c4e54dcb330d67beef4f93acdfa8a26f39af1bc1c4f642a results/run-1-block-start.json +bb4047216e7ab3f6e41507bc167ef281be3a85e7694312269e7350ab5642d7c3 results/run-1-block.csv +9b59ba196116e27d5de0a83bb1b06cf5acaec4930babe0ead12389fedca4f6b3 results/run-2-allow-end.json +db9298d58b5fdf3c082422ae729b7d05b0d4719f5f3392072e5bb61fd3aa4466 results/run-2-allow-start.json +99fa208a15314ecf65675adab4f178d4b3ba600f9c8c43b4c06df70d94ecaa2d results/run-2-allow.csv +0e929531bc267972059b671e3e0f5a84a0931473ec00bd4d382b4fbffa8c5f1b results/run-2-baseline.csv +337329cbd5eb256bc3755bed597be49e6e9948ae445dea55c8cd65dcb6bae3ef results/run-2-block-end.json +271819b5b1ec995a656952f71b90434aeb52038818243f0712c75fdd51fbf267 results/run-2-block-start.json +8b345a7f6569a50aa45f51854effc96fed061e3ffd1404568d5f5e428f34f054 results/run-2-block.csv +9b59ba196116e27d5de0a83bb1b06cf5acaec4930babe0ead12389fedca4f6b3 results/run-3-allow-end.json +db9298d58b5fdf3c082422ae729b7d05b0d4719f5f3392072e5bb61fd3aa4466 results/run-3-allow-start.json +62b75edc3084aaca6ec510224805e2a5f7b1cda5d39f2bfae59edd3678d4656c results/run-3-allow.csv +9436e862238f13d23f92a9d31249e878a37689233b41514dcd01b16459e9fc30 results/run-3-baseline.csv +337329cbd5eb256bc3755bed597be49e6e9948ae445dea55c8cd65dcb6bae3ef results/run-3-block-end.json +271819b5b1ec995a656952f71b90434aeb52038818243f0712c75fdd51fbf267 results/run-3-block-start.json +8f1e994dde69f7b2e152b07d69e46ca9f9c2e39ef412608dba0d37d0a8f88f08 results/run-3-block.csv +421a244564c34296e185cdcf70d2269227ae102f2e7eeef94afddd495c4e06c2 results/status.txt +1921846910314078141a41cb6284825266c39dad955c9550f187d50d86e81998 results/update-layer-evidence.jsonl +37fb6904743ebb615cc676a0b77b882b94d0152a64b53e2a3da429202b557e4a summary.csv +4d91f1eb0d231658a790ecd4ce9ac994c58ea028f26368ae7a38782e988cff83 summary.json +0a83d3cb04e61b2bf7f99249fc70438e9a4c27c000ac504ef47884ef9ed40250 validation.json +08e31b82234bbc30665c268851faa7d3a9586d06fba516dabb2cb207f1807f84 verify.py diff --git a/docs/benchmarks/2026-09-29-v0.2.0/analyze.py b/docs/benchmarks/2026-09-29-v0.2.0/analyze.py new file mode 100644 index 0000000..2720541 --- /dev/null +++ b/docs/benchmarks/2026-09-29-v0.2.0/analyze.py @@ -0,0 +1,147 @@ +"""Validate all samples and compute reproducible nearest-rank descriptive statistics.""" +import csv, json, math, statistics +from pathlib import Path + +root = Path(__file__).resolve().parent +folder = root / 'results' +meta = json.loads((folder / 'metadata.json').read_text(encoding='utf-8-sig')) +provenance = json.loads((root/'provenance.json').read_text(encoding='utf-8-sig')) if (root/'provenance.json').exists() else {} +assert (folder / 'status.txt').read_text(encoding='utf-8-sig').strip() == 'SUCCESS' +metrics = ['cpu_pct','private_mib','working_set_mib','threads','handles', + 'io_read_bytes_s','io_write_bytes_s','system_cpu_pct','collector_cpu_pct','interval_s'] +all_rows = [] +checks = [] +layer_sets = {} +for run in range(1, meta['repeats'] + 1): + for scenario in ['baseline','allow','block']: + label = f'run-{run}-{scenario}' + with (folder / f'{label}.csv').open(encoding='utf-8-sig', newline='') as stream: + rows = list(csv.DictReader(stream)) + assert len(rows) == meta['seconds'], (label, len(rows)) + assert [int(r['sample']) for r in rows] == list(range(1,len(rows)+1)) + assert all(r['scenario']==scenario and int(r['run'])==run for r in rows) + for r in rows: + for key in metrics + ['elapsed_s','process_cpu_s']: + r[key] = float(r[key]) if r[key] else None + assert r[key] is None or math.isfinite(r[key]) + assert 0 < r['interval_s'] <= 1.25, (label, 'Sampling interval outside campaign tolerance') + assert 0 <= r['system_cpu_pct'] <= 100 + assert all(rows[i]['elapsed_s'] > rows[i-1]['elapsed_s'] for i in range(1,len(rows))) + assert abs(rows[-1]['elapsed_s'] - meta['seconds']) <= 1.25, (label, 'Incorrect phase duration') + # The stopwatch starts before the initial counter snapshot; retain that offset. + initial_offset = rows[0]['elapsed_s'] - rows[0]['interval_s'] + assert 0 <= initial_offset < 0.1, (label, 'Unexpected initial timing offset') + assert abs(sum(r['interval_s'] for r in rows) - (rows[-1]['elapsed_s'] - initial_offset)) < 0.000001, (label, 'Inconsistent elapsed time') + if scenario != 'baseline': + assert len({r['pid'] for r in rows}) == 1 + assert all(0 <= r['cpu_pct'] <= 100 and r['private_mib'] > 0 for r in rows) + start=json.loads((folder/f'{label}-start.json').read_text(encoding='utf-8-sig')) + end=json.loads((folder/f'{label}-end.json').read_text(encoding='utf-8-sig')) + assert start['state'] == end['state'] == scenario + assert start['layers'] == end['layers'], label + assert start['layers'] == layer_sets.setdefault(scenario, start['layers']), label + assert end['blocked_count'] == start['blocked_count'], (label, 'Unexpected shutdown activity') + assert end['uptime_seconds'] >= start['uptime_seconds'] + meta['seconds'] - 2 + checks.append({'run':run,'scenario':scenario,'samples':len(rows), + 'elapsed_s':rows[-1]['elapsed_s'], + 'intervals_over_1_25s':sum(r['interval_s']>1.25 for r in rows), + 'max_interval_s':max(r['interval_s'] for r in rows)}) + all_rows.extend(rows) + +summaries=[] +for scope in ['pooled', *range(1, meta['repeats']+1)]: + for scenario in ['baseline','allow','block']: + rows=[r for r in all_rows if r['scenario']==scenario and (scope=='pooled' or int(r['run'])==scope)] + for metric in metrics: + values=sorted(r[metric] for r in rows if r[metric] is not None) + if not values: continue + mean=statistics.mean(values) + if metric in ['cpu_pct','collector_cpu_pct','system_cpu_pct','io_read_bytes_s','io_write_bytes_s']: + mean=sum(r[metric]*r['interval_s'] for r in rows)/sum(r['interval_s'] for r in rows) + summaries.append(dict(scope=scope,scenario=scenario,metric=metric,n=len(values),mean=mean, + median=statistics.median(values),p95=values[math.ceil(.95*len(values))-1], + maximum=max(values),minimum=min(values))) +with (root/'summary.csv').open('w',encoding='utf-8',newline='') as stream: + writer=csv.DictWriter(stream,fieldnames=list(summaries[0]));writer.writeheader();writer.writerows(summaries) +(root/'validation.json').write_text(json.dumps({'passed':True,'samples':len(all_rows),'runs':checks},indent=2)) +(root/'summary.json').write_text(json.dumps(summaries,indent=2)) +def stat(scenario, metric, scope='pooled'): + return next(s for s in summaries if s['scope']==scope and s['scenario']==scenario and s['metric']==metric) + +comparison = ['', '## Comparaison des moyennes', '', + '| Mesure | Allow | Block |', '|---|---:|---:|'] +for metric, label, digits in [('working_set_mib', 'Mémoire résidente (MiB)', 2), + ('private_mib', 'Mémoire privée (MiB)', 2), + ('cpu_pct', 'CPU (% du total machine)', 4)]: + comparison.append(f"| {label} | {stat('allow', metric)['mean']:.{digits}f} | {stat('block', metric)['mean']:.{digits}f} |") +comparison += ['', '**Allow** : protections désactivées. **Block** : protections actives en arrière-plan. Les valeurs CPU sont des pourcentages de la capacité totale de la machine.', ''] +lines = ['# Benchmark Wardoff 0.2.0', '', *comparison, + f"Mesures locales commencées le {meta['startedUtc'][:10]} (UTC). Binaire officiel de la dernière release disponible au moment de cette campagne, vérifié avant exécution.", '', + '## Résultats', '', + '| Scénario | Mesure | Moyenne | Médiane | P95 | Maximum |', + '|---|---|---:|---:|---:|---:|'] +for scenario in ['allow','block']: + for metric, label in [('private_mib','Mémoire privée (MiB)'),('working_set_mib','Working Set (MiB)'),('cpu_pct','CPU Wardoff (% du total machine)'),('threads','Threads')]: + s=stat(scenario,metric) + digits=5 if metric=='cpu_pct' else 3 + vals=' | '.join(f'{s[k]:.{digits}f}' for k in ['mean','median','p95','maximum']) + lines.append(f'| {scenario.title()} | {label} | {vals} |') +lines += ['', 'Les moyennes CPU sont pondérées par la durée réelle des intervalles. Médiane, P95 et maximum décrivent les échantillons d’environ une seconde. Un maximum ici ne représente pas un pic instantané.', '', + '## Répétitions et activité ambiante', '', + '| Répétition | Scénario | CPU Wardoff moyen (%) | Working Set moyen (MiB) | CPU système moyen (%) |', + '|---|---|---:|---:|---:|'] +for run in range(1,meta['repeats']+1): + for scenario in ['baseline','allow','block']: + cpu='—' if scenario=='baseline' else f"{stat(scenario,'cpu_pct',run)['mean']:.5f}" + ram='—' if scenario=='baseline' else f"{stat(scenario,'working_set_mib',run)['mean']:.3f}" + system=stat(scenario,'system_cpu_pct',run)['mean'] + lines.append(f'| {run} | {scenario} | {cpu} | {ram} | {system:.3f} |') +lines += ['', 'La baseline sert à documenter l’activité ambiante. Elle n’est pas soustraite aux mesures du processus. Le CPU système inclut toutes les applications, dont le collecteur et l’environnement de travail ; il ne mesure pas le coût causal de Wardoff.', '', + '## Protocole', '', + f"- {meta['repeats']} répétitions × 3 scénarios × {meta['seconds']} secondes : {meta['repeats']*3*meta['seconds']/60:g} minutes mesurées, {meta['repeats']*meta['seconds']} échantillons par scénario.", + '- Baseline : aucun processus Wardoff. Allow : icône visible, protections désactivées. Block : même interface visible, protections en fonctionnement normal, sans tentative réelle d’arrêt.', + '- Ordres : baseline/allow/block, allow/block/baseline, block/baseline/allow. Nouveau processus pour chaque phase avec Wardoff ; stabilisation de 30 secondes exclue avant chaque mesure.', + f"- Échantillonnage sur horloge monotone, cible d’une seconde. CPU = différence de temps processeur / temps réel / {meta['logicalProcessors']} processeurs logiques × 100. 100 % correspond à toute la machine ; multiplier par {meta['logicalProcessors']} pour la convention où un cœur logique vaut 100 %.", + '- Mémoire privée : PrivateMemorySize64, engagement privé du processus, pas nécessairement résident. Working Set : mémoire résidente du processus, pages partagées incluses. Ce ne sont pas deux mesures à additionner. 1 MiB = 1 048 576 octets.', + '- P95 : rang le plus proche par excès (ceil(0,95 × N)). Statistiques descriptives ; les secondes consécutives ne sont pas des répétitions indépendantes.', + '- État et couches contrôlés hors mesure au début et à la fin ; sortie normale via WM_QUIT, avec attente de fermeture. Aucun arrêt Windows, redémarrage, suspension, changement de démarrage automatique ou fermeture d’applications tierces.', + '- Le collecteur empêche la veille système de manière identique dans tous les scénarios ; le plan d’alimentation reste inchangé. Données conservées en mémoire pendant chaque phase, exportées entre phases.', '', + '## Machine et provenance', '', + f"- CPU : {meta['cpu'][0]['Name']}, {sum(c['NumberOfCores'] for c in meta['cpu'])} cœurs / {meta['logicalProcessors']} processeurs logiques.", + f"- RAM visible par Windows : {meta['os']['TotalVisibleMemorySize']/1024/1024:.3f} GiB (machine équipée de 16 Go).", + f"- Système : {meta['os']['Caption']}, {meta['os']['Version']}, build {provenance.get('osBuild',meta['os']['BuildNumber'])} (révision vérifiée après mesure).", + f"- Alimentation : {meta['powerPlan']}. Exécution administrateur : {meta['admin']}.", + '- Release : [v0.2.0](https://github.com/RomainROCH/Wardoff/releases/tag/v0.2.0). Version affichée par le binaire : wardoff 0.2.0.', + f"- SHA-256 du binaire : `{meta['binarySha256'].lower()}` ; correspond au digest de l’asset GitHub.", + f"- Commit du tag : `{meta['releaseCommit']}`. HEAD local/main distant vérifié : `{meta['mainCommit']}`.", + '- Après récupération des références distantes, aucune différence de code applicatif, manifeste, script de build ou dépendances entre le tag, main et dev. Les différences concernent la documentation et les consignes.', + '- Tentative de reconstruction avec cargo build --release --locked refusée par Cargo car le verrou des dépendances nécessite une mise à jour. Les mesures concernent donc explicitement le binaire distribué, pas une reconstruction locale. Le rattachement à la release et son digest sont vérifiés ; aucune reproductibilité binaire n’est revendiquée.', '', + '## Couches réellement actives', ''] +block_status=json.loads((folder/'run-1-block-start.json').read_text(encoding='utf-8-sig')) +for layer, active in block_status['layers'].items(): + lines.append(f"- `{layer}` : {'active' if active else 'inactive'}.") +lines += ['', 'Le journal du runtime administrateur confirme que la tâche `Microsoft\\Windows\\UpdateOrchestrator\\Reboot` est absente sur cette machine LTSC. La couche Update est donc ignorée normalement. Ces résultats couvrent seulement cette combinaison effective de couches. La surveillance locale signalée active par le runtime ne constitue pas une preuve d’efficacité contre un arrêt forcé.', '', + '## Contrôles et limites', '', + f"- Validation : {len(all_rows)} échantillons, {len(checks)} phases complètes, état et identité du processus vérifiés.", + f"- Intervalles > 1,25 s : {sum(c['intervals_over_1_25s'] for c in checks)} ; intervalle maximal : {max(c['max_interval_s'] for c in checks):.3f} s.", + '- Le CPU du collecteur figure dans summary.csv et dans les données brutes. Il reste inclus dans le CPU système.', + f"- CPU système pendant les baselines : moyenne {stat('baseline','system_cpu_pct')['mean']:.2f} %, P95 {stat('baseline','system_cpu_pct')['p95']:.2f} %, maximum {stat('baseline','system_cpu_pct')['maximum']:.2f} %. Le bureau n’était donc pas au repos absolu. Aucun échantillon n’a été supprimé.", + f"- CPU moyen du collecteur : baseline {stat('baseline','collector_cpu_pct')['mean']:.3f} %, Allow {stat('allow','collector_cpu_pct')['mean']:.3f} %, Block {stat('block','collector_cpu_pct')['mean']:.3f} % du total machine. Son coût reste distinct du processus Wardoff.", + '- Les moyennes CPU et Working Set ont été recalculées indépendamment en PowerShell à partir des CSV ; elles concordent avec les résultats Python.', + '- Le processus CPU peut être quantifié par le compteur Windows : un échantillon à zéro ne prouve pas une absence absolue de travail. La moyenne intégrée sur 15 minutes par mode est plus informative.', + '- Mesure sur une machine utilisée normalement, avec ses services et applications ouverts, pas sur une image Windows isolée. Pas de contrôle des températures, fréquences instantanées ou activité utilisateur. Ne pas attribuer une différence de CPU système à Wardoff.', + '- Démarrage exclu ; pas de test de charge de commandes, de vrai blocage d’arrêt, de batterie ou de fuite mémoire sur plusieurs heures. Résultats non généralisables à toutes les machines ou toutes les protections.', + '- Les compteurs I/O sont ceux du processus (fichiers, périphériques, réseau), pas une mesure du trafic physique du disque.', '', + '## Fichiers et reproduction', '', + '- `Measure-Wardoff.ps1` : collecteur autonome PowerShell ; lancer en administrateur avec le binaire vérifié dans le même dossier.', + '- `results/` : CSV bruts, états JSON, métadonnées, journal des phases et statut final.', + '- `analyze.py` : validation et calcul des statistiques avec Python standard, sans dépendances supplémentaires.', + '- `summary.csv`, `summary.json`, `validation.json` : résultats et contrôles reproductibles.', + '- Le pilote technique court est exclu des statistiques et de ce dossier de preuves ; il reste dans l’archive locale initiale.', + '- L’empreinte du collecteur n’a pas été enregistrée au démarrage de la campagne. Le script conservé est identique à la copie de l’archive initiale ; le manifeste ultérieur protège cette copie sans constituer une attestation cryptographique de son exécution.', '', + 'Ce dossier conserve les preuves de la campagne. Son état de sauvegarde distante doit être vérifié par la référence Git distante ; la présence de ces fichiers ne constitue pas à elle seule une sauvegarde hors machine.', ''] +lines += ['## Formulation utilisable avec son contexte', '', + '> On an Intel Core i7-6700HQ running Windows 11 IoT Enterprise LTSC, Wardoff 0.2.0 averaged approximately 14 MiB resident memory and less than 0.01% total-system CPU in Block mode across three five-minute background runs. UpdateOrchestrator task protection was unavailable because the task was absent.', '', + 'Cette formulation décrit une mesure du processus sur cette machine ; elle ne promet ni la même consommation partout, ni une protection contre tous les arrêts Windows.', ''] +(root/'RAPPORT.md').write_text('\n'.join(lines),encoding='utf-8') +print(json.dumps([s for s in summaries if s['scope']=='pooled'],indent=2)) diff --git a/docs/benchmarks/2026-09-29-v0.2.0/binary.json b/docs/benchmarks/2026-09-29-v0.2.0/binary.json new file mode 100644 index 0000000..d1d5c6a --- /dev/null +++ b/docs/benchmarks/2026-09-29-v0.2.0/binary.json @@ -0,0 +1,13 @@ +{ + "version": "0.2.0", + "filename": "wardoff.exe", + "sizeBytes": 1825280, + "sha256": "96d1700c492b1689e6cb3a18847481eb9a203e30db4d4ba273d4b458ed6a84bc", + "releaseUrl": "https://github.com/RomainROCH/Wardoff/releases/tag/v0.2.0", + "downloadUrl": "https://github.com/RomainROCH/Wardoff/releases/download/v0.2.0/wardoff.exe", + "releasePublishedAt": "2026-05-03T10:53:31Z", + "sourceTag": "v0.2.0", + "sourceCommit": "9f6c0375c851d0a5b933da0bd69bafc75b215cf5", + "verification": "Downloaded executable hash matched the GitHub asset digest before measurement and again after measurement; --version returned wardoff 0.2.0.", + "limitation": "Source association is through the release tag; no reproducible-build claim. Local cargo build --release --locked failed because the checked-in lockfile required an update." +} diff --git a/docs/benchmarks/2026-09-29-v0.2.0/provenance.json b/docs/benchmarks/2026-09-29-v0.2.0/provenance.json new file mode 100644 index 0000000..9d635a1 --- /dev/null +++ b/docs/benchmarks/2026-09-29-v0.2.0/provenance.json @@ -0,0 +1,19 @@ +{ + "checkedUtc": "2026-09-29T02:29:58.5370137Z", + "branch": "main", + "head": "aa40e8982273fa66b86b806ec1424a547affdbd3", + "upstream": "origin/main", + "workingTreeClean": true, + "sourceTreeRelease": "89c6f08a6b9e010034ba312473655893b9345e69", + "sourceTreeMain": "89c6f08a6b9e010034ba312473655893b9345e69", + "sourceTreeDev": "89c6f08a6b9e010034ba312473655893b9345e69", + "remoteRefs": [ + "aa40e8982273fa66b86b806ec1424a547affdbd3\tHEAD", + "2c6d772d4ecfcb0a30a37b5cd345cd5b39167ddf\trefs/heads/dev", + "aa40e8982273fa66b86b806ec1424a547affdbd3\trefs/heads/main", + "9f6c0375c851d0a5b933da0bd69bafc75b215cf5\trefs/tags/v0.2.0" + ], + "wardoffRunning": false, + "osBuild": "26100.9445", + "physicalRamBytes": 17179869184 +} diff --git a/docs/benchmarks/2026-09-29-v0.2.0/results/metadata.json b/docs/benchmarks/2026-09-29-v0.2.0/results/metadata.json new file mode 100644 index 0000000..e475d57 --- /dev/null +++ b/docs/benchmarks/2026-09-29-v0.2.0/results/metadata.json @@ -0,0 +1,32 @@ +{ + "startedUtc": "2026-09-29T01:38:30.4908143Z", + "seconds": 300, + "warmupSeconds": 30, + "repeats": 3, + "admin": true, + "cpu": [ + { + "Name": "Intel(R) Core(TM) i7-6700HQ CPU @ 2.60GHz", + "NumberOfCores": 4, + "NumberOfLogicalProcessors": 8 + } + ], + "os": { + "Caption": "Microsoft Windows 11 IoT Enterprise LTSC", + "Version": "10.0.26100", + "BuildNumber": "26100", + "TotalVisibleMemorySize": 16637696 + }, + "logicalProcessors": 8, + "powerPlan": "Power Scheme GUID: 381b4222-f694-41f0-9685-ff5bb260df2e (Balanced)", + "binarySha256": "96D1700C492B1689E6CB3A18847481EB9A203E30DB4D4BA273D4B458ED6A84BC", + "version": "0.2.0", + "releaseTag": "v0.2.0", + "releaseCommit": "9f6c0375c851d0a5b933da0bd69bafc75b215cf5", + "mainCommit": "aa40e8982273fa66b86b806ec1424a547affdbd3", + "cpuDefinition": "100 * process CPU seconds / actual elapsed seconds / logical processors", + "ramUnit": "MiB = 1048576 bytes", + "ioDefinition": "Process I/O includes file/device/network operations, not physical disk traffic", + "p95Definition": "nearest rank", + "collectorPid": 16356 +} diff --git a/docs/benchmarks/2026-09-29-v0.2.0/results/progress.log b/docs/benchmarks/2026-09-29-v0.2.0/results/progress.log new file mode 100644 index 0000000..32462fa --- /dev/null +++ b/docs/benchmarks/2026-09-29-v0.2.0/results/progress.log @@ -0,0 +1,18 @@ +2026-09-29T03:38:30.6211706+02:00 START run-1-baseline +2026-09-29T03:44:00.6786970+02:00 COMPLETE run-1-baseline samples=300 +2026-09-29T03:44:00.6817761+02:00 START run-1-allow +2026-09-29T03:49:33.9134732+02:00 COMPLETE run-1-allow samples=300 +2026-09-29T03:49:33.9155803+02:00 START run-1-block +2026-09-29T03:55:07.0833654+02:00 COMPLETE run-1-block samples=300 +2026-09-29T03:55:07.0853594+02:00 START run-2-allow +2026-09-29T04:00:40.2110218+02:00 COMPLETE run-2-allow samples=300 +2026-09-29T04:00:40.2132790+02:00 START run-2-block +2026-09-29T04:06:13.3630262+02:00 COMPLETE run-2-block samples=300 +2026-09-29T04:06:13.3652464+02:00 START run-2-baseline +2026-09-29T04:11:43.6871983+02:00 COMPLETE run-2-baseline samples=300 +2026-09-29T04:11:43.6892391+02:00 START run-3-block +2026-09-29T04:17:16.8447559+02:00 COMPLETE run-3-block samples=300 +2026-09-29T04:17:16.8461691+02:00 START run-3-baseline +2026-09-29T04:22:46.8746987+02:00 COMPLETE run-3-baseline samples=300 +2026-09-29T04:22:46.8768976+02:00 START run-3-allow +2026-09-29T04:28:19.9941616+02:00 COMPLETE run-3-allow samples=300 diff --git a/docs/benchmarks/2026-09-29-v0.2.0/results/run-1-allow-end.json b/docs/benchmarks/2026-09-29-v0.2.0/results/run-1-allow-end.json new file mode 100644 index 0000000..14f27af --- /dev/null +++ b/docs/benchmarks/2026-09-29-v0.2.0/results/run-1-allow-end.json @@ -0,0 +1,12 @@ +{ + "state": "allow", + "layers": { + "shutdown": false, + "local_shutdown": false, + "update": false, + "remote": false, + "sleep": false + }, + "uptime_seconds": 333, + "blocked_count": 0 +} diff --git a/docs/benchmarks/2026-09-29-v0.2.0/results/run-1-allow-start.json b/docs/benchmarks/2026-09-29-v0.2.0/results/run-1-allow-start.json new file mode 100644 index 0000000..d41852d --- /dev/null +++ b/docs/benchmarks/2026-09-29-v0.2.0/results/run-1-allow-start.json @@ -0,0 +1,12 @@ +{ + "state": "allow", + "layers": { + "shutdown": false, + "local_shutdown": false, + "update": false, + "remote": false, + "sleep": false + }, + "uptime_seconds": 32, + "blocked_count": 0 +} diff --git a/docs/benchmarks/2026-09-29-v0.2.0/results/run-1-allow.csv b/docs/benchmarks/2026-09-29-v0.2.0/results/run-1-allow.csv new file mode 100644 index 0000000..67afdf6 --- /dev/null +++ b/docs/benchmarks/2026-09-29-v0.2.0/results/run-1-allow.csv @@ -0,0 +1,301 @@ +"run","scenario","sample","utc","elapsed_s","interval_s","system_cpu_pct","collector_cpu_pct","pid","process_cpu_s","cpu_pct","private_mib","working_set_mib","threads","handles","io_read_bytes_s","io_write_bytes_s" +"1","allow","1","2026-09-29T01:44:34.8208793Z","1.0071993","1.0071959","7.03125","0","12788","0","0","2","12.57421875","8","176","0","0" +"1","allow","2","2026-09-29T01:44:35.8147327Z","2.0007542","0.9935549","6.8359375","0.393158948740528","12788","0","0","2","12.57421875","8","176","0","0" +"1","allow","3","2026-09-29T01:44:36.8275183Z","3.0135132","1.012759","6.73076923076923","0","12788","0","0","2","12.57421875","8","176","0","0" +"1","allow","4","2026-09-29T01:44:37.8265025Z","4.0125915","0.9990783","8.59375","0","12788","0","0","2","12.57421875","8","176","0","0" +"1","allow","5","2026-09-29T01:44:38.8292329Z","5.0153445","1.002753","7.421875","0","12788","0","0","2","12.57421875","8","176","0","0" +"1","allow","6","2026-09-29T01:44:39.8218578Z","6.0078652","0.9925207","13.4920634920635","0","12788","0","0","2","12.57421875","8","176","0","0" +"1","allow","7","2026-09-29T01:44:40.8215352Z","7.0075465","0.999681300000001","14.0625","0.195374765937904","12788","0","0","2","12.57421875","8","176","0","0" +"1","allow","8","2026-09-29T01:44:41.8329992Z","8.0189825","1.011436","8.65384615384615","0.193104160816898","12788","0","0","2","12.57421875","8","176","0","0" +"1","allow","9","2026-09-29T01:44:42.8193121Z","9.0054358","0.986453299999999","4.36507936507936","0","12788","0","0","2","12.57421875","8","176","0","0" +"1","allow","10","2026-09-29T01:44:43.8287856Z","10.0149963","1.0095605","7.11538461538461","0","12788","0","0","2","12.57421875","8","176","0","0" +"1","allow","11","2026-09-29T01:44:44.8256384Z","11.0116241","0.996627799999999","17.1875","0","12788","0","0","2","12.57421875","8","176","0","0" +"1","allow","12","2026-09-29T01:44:45.8248034Z","12.0108113","0.999187200000001","41.2109375","0","12788","0","0","2","12.57421875","8","176","0","0" +"1","allow","13","2026-09-29T01:44:46.8195620Z","13.0058502","0.995038899999999","24.4047619047619","0","12788","0","0","2","12.57421875","8","176","0","0" +"1","allow","14","2026-09-29T01:44:47.8301866Z","14.0164084","1.0105582","17.1153846153846","0.193271896660677","12788","0","0","2","12.57421875","8","176","0","0" +"1","allow","15","2026-09-29T01:44:48.8150721Z","15.0010277","0.9846193","7.73809523809524","0","12788","0","0","2","12.57421875","8","176","0","0" +"1","allow","16","2026-09-29T01:44:49.8254435Z","16.0115384","1.0105107","10.2119460500963","0","12788","0","0","2","12.57421875","8","176","0","0" +"1","allow","17","2026-09-29T01:44:50.8236009Z","17.0096437","0.998105299999999","12.4756335282651","0","12788","0","0","2","12.57421875","8","176","0","0" +"1","allow","18","2026-09-29T01:44:51.8204289Z","18.0065532","0.996909500000001","12.3015873015873","0","12788","0","0","2","12.57421875","8","176","0","0" +"1","allow","19","2026-09-29T01:44:52.8204953Z","19.0065256","0.999972400000001","8.43137254901961","0.195317890773785","12788","0","0","2","12.57421875","8","176","0","0" +"1","allow","20","2026-09-29T01:44:53.8205382Z","20.0067029","1.0001773","8.94941634241245","0","12788","0","0","2","12.57421875","8","176","0","0" +"1","allow","21","2026-09-29T01:44:54.8214131Z","21.0075635","1.0008606","4.8828125","0.195144558592875","12788","0","0","2","12.57421875","8","176","0","0" +"1","allow","22","2026-09-29T01:44:55.8247459Z","22.0107565","1.003193","7.69230769230769","0.194690852109215","12788","0","0","2","12.57421875","8","176","0","0" +"1","allow","23","2026-09-29T01:44:56.8270108Z","23.0130389","1.0022824","7.421875","0","12788","0","0","2","12.57421875","8","176","0","0" +"1","allow","24","2026-09-29T01:44:57.8273094Z","24.0133544","1.0003155","10.7421875","0","12788","0","0","2","12.57421875","8","176","0","0" +"1","allow","25","2026-09-29T01:44:58.8135901Z","24.9996121","0.986257700000003","5.95238095238095","0","12788","0","0","2","12.57421875","8","176","0","0" +"1","allow","26","2026-09-29T01:44:59.8203213Z","26.0064104","1.0067983","6.4453125","0","12788","0","0","2","12.57421875","8","176","0","0" +"1","allow","27","2026-09-29T01:45:00.8189653Z","27.0049596","0.998549199999999","8.3984375","0.195596271070069","12788","0","0","1.97265625","12.55859375","7","176","0","0" +"1","allow","28","2026-09-29T01:45:01.8195956Z","28.0056817","1.0007221","10.7421875","0","12788","0","0","1.97265625","12.55859375","7","176","0","0" +"1","allow","29","2026-09-29T01:45:02.8326358Z","29.0187666","1.0130849","17.3076923076923","0","12788","0","0","1.97265625","12.55859375","7","176","0","0" +"1","allow","30","2026-09-29T01:45:03.8294050Z","30.0152776","0.996510999999998","18.5546875","0","12788","0","0","1.97265625","12.55859375","7","176","0","0" +"1","allow","31","2026-09-29T01:45:04.8259252Z","31.0120128","0.9967352","11.328125","0","12788","0","0","1.97265625","12.55859375","7","176","0","0" +"1","allow","32","2026-09-29T01:45:05.8207280Z","32.0067061","0.994693299999998","33.1349206349206","0","12788","0","0","1.97265625","12.55859375","7","176","0","0" +"1","allow","33","2026-09-29T01:45:06.8190654Z","33.0051886","0.998482500000001","5.859375","0.195609337169154","12788","0","0","1.97265625","12.55859375","7","176","0","0" +"1","allow","34","2026-09-29T01:45:07.8314215Z","34.0174828","1.0122942","10.7692307692308","0","12788","0","0","1.97265625","12.55859375","7","176","0","0" +"1","allow","35","2026-09-29T01:45:08.8282068Z","35.0142989","0.996816100000004","7.8125","0","12788","0","0","1.97265625","12.55859375","7","176","0","0" +"1","allow","36","2026-09-29T01:45:09.8259210Z","36.0119269","0.997627999999999","6.0546875","0","12788","0","0","1.97265625","12.55859375","7","176","0","0" +"1","allow","37","2026-09-29T01:45:10.8261807Z","37.0122356","1.0003087","5.2734375","0","12788","0","0","1.97265625","12.55859375","7","176","0","0" +"1","allow","38","2026-09-29T01:45:11.8292992Z","38.0154371","1.0032015","6.4453125","0","12788","0","0","1.97265625","12.55859375","7","176","0","0" +"1","allow","39","2026-09-29T01:45:12.8268343Z","39.0127586","0.997321499999998","14.2578125","0","12788","0","0","1.97265625","12.55859375","7","176","0","0" +"1","allow","40","2026-09-29T01:45:13.8196105Z","40.0055856","0.992826999999998","27.1825396825397","0","12788","0","0","1.97265625","12.55859375","7","176","0","0" +"1","allow","41","2026-09-29T01:45:14.8301554Z","41.016229","1.0106434","16.9230769230769","0","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","42","2026-09-29T01:45:15.8163257Z","42.002306","0.986077000000002","4.16666666666667","0","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","43","2026-09-29T01:45:16.8275248Z","43.0137178","1.0114118","6.34615384615385","0","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","44","2026-09-29T01:45:17.8278421Z","44.0140042","1.0002864","10.9375","0","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","45","2026-09-29T01:45:18.8296378Z","45.015739","1.0017348","6.8359375","0","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","46","2026-09-29T01:45:19.8188580Z","46.0048705","0.989131499999999","3.76984126984127","0","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","47","2026-09-29T01:45:20.8191545Z","47.0052261","1.0003556","6.0546875","0","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","48","2026-09-29T01:45:21.8312880Z","48.0172922","1.01206610000001","7.30769230769231","0","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","49","2026-09-29T01:45:22.8285978Z","49.0141529","0.996860699999999","5.859375","0","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","50","2026-09-29T01:45:23.8285962Z","50.0146478","1.0004949","6.0546875","0","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","51","2026-09-29T01:45:24.8146175Z","51.0007159","0.986068099999997","4.76190476190476","0","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","52","2026-09-29T01:45:25.8216398Z","52.0078996","1.0071837","7.03125","0","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","53","2026-09-29T01:45:26.8220548Z","53.0080225","1.0001229","9.5703125","0.195288499043467","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","54","2026-09-29T01:45:27.8232959Z","54.0092677","1.0012452","11.1328125","0.195069599334908","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","55","2026-09-29T01:45:28.8185483Z","55.0045049","0.995237200000005","7.6171875","0.196247186097946","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","56","2026-09-29T01:45:29.8185066Z","56.0045875","1.0000826","6.0665362035225","0","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","57","2026-09-29T01:45:30.8333859Z","57.0193056","1.0147181","5.75815738963532","0","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","58","2026-09-29T01:45:31.8186206Z","58.0045872","0.9852816","5.35714285714286","0","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","59","2026-09-29T01:45:32.8335171Z","59.0195652","1.014978","10","0","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","60","2026-09-29T01:45:33.8337594Z","60.0197818","1.0002166","7.6171875","0","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","61","2026-09-29T01:45:34.8206719Z","61.0065752","0.986793400000003","6.34920634920635","0","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","62","2026-09-29T01:45:35.8201516Z","62.006109","0.999533799999995","5.078125","0","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","63","2026-09-29T01:45:36.8249810Z","63.0111272","1.0050182","9.23076923076923","0","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","64","2026-09-29T01:45:37.8194817Z","64.0054392","0.994312000000001","14.0873015873016","0","12788","0.015625","0.196429792660654","1.97265625","12.515625","7","176","0","0" +"1","allow","65","2026-09-29T01:45:38.8282011Z","65.0142717","1.0088325","10.3846153846154","0","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","66","2026-09-29T01:45:39.8143885Z","66.0003684","0.986096700000004","5.75396825396825","0","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","67","2026-09-29T01:45:40.8171172Z","67.0038483","1.0034799","4.8828125","0","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","68","2026-09-29T01:45:41.8329013Z","68.0189734","1.01512509999999","5.76923076923077","0","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","69","2026-09-29T01:45:42.8219484Z","69.0079333","0.988959899999998","4.96031746031746","0","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","70","2026-09-29T01:45:43.8221185Z","70.0081749","1.00024160000001","9.9609375","0.195265323897744","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","71","2026-09-29T01:45:44.8208321Z","71.0067852","0.998610299999996","6.45792563600783","0.195584303506584","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","72","2026-09-29T01:45:45.8188586Z","72.0048121","0.998026899999999","8.96686159844055","0.195698632972719","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","73","2026-09-29T01:45:46.8239350Z","73.0098602","1.0050481","8.26923076923077","0","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","74","2026-09-29T01:45:47.8251015Z","74.0112474","1.00138720000001","9.765625","0","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","75","2026-09-29T01:45:48.8283921Z","75.0143428","1.00309539999999","16.40625","0","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","76","2026-09-29T01:45:49.8178668Z","76.0042844","0.989941600000009","10.7142857142857","0","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","77","2026-09-29T01:45:50.8202002Z","77.0060704","1.001786","6.4453125","0","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","78","2026-09-29T01:45:51.8332638Z","78.0191806","1.0131102","5.76923076923077","0","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","79","2026-09-29T01:45:52.8299633Z","79.0160211","0.996840500000005","5.2734375","0","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","80","2026-09-29T01:45:53.8158217Z","80.0018741","0.985852999999992","7.73809523809524","0","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","81","2026-09-29T01:45:54.8326368Z","81.0188789","1.0170048","7.30769230769231","0","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","82","2026-09-29T01:45:55.8299564Z","82.0159042","0.997025300000004","19.7265625","0","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","83","2026-09-29T01:45:56.8992626Z","83.040758","1.0248538","32.6530612244898","0.190575963127618","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","84","2026-09-29T01:45:57.8212419Z","84.0073544","0.9665964","23.6897274633124","0.202062101617593","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","85","2026-09-29T01:45:58.8213728Z","85.0075118","1.00015740000001","17.96875","0.195281762650558","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","86","2026-09-29T01:45:59.8238428Z","86.0098747","1.00236289999999","10.5769230769231","0.194852084010692","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","87","2026-09-29T01:46:00.8249782Z","87.0110942","1.0012195","8.0078125","0","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","88","2026-09-29T01:46:01.8242898Z","88.0104581","0.999363899999992","10.9375","0","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","89","2026-09-29T01:46:02.8255183Z","89.0114308","1.00097270000001","7.421875","0","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","90","2026-09-29T01:46:03.8290937Z","90.0152979","1.00386709999999","5.46875","0","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","91","2026-09-29T01:46:04.8150543Z","91.0010184","0.985720499999999","5.75396825396825","0","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","92","2026-09-29T01:46:05.8206358Z","92.0072992","1.0062808","9.375","0","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","93","2026-09-29T01:46:06.8275797Z","93.0138921","1.0065929","10.3846153846154","0.194033258132458","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","94","2026-09-29T01:46:07.8278337Z","94.0140226","1.0001305","4.1015625","0","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","95","2026-09-29T01:46:08.8144138Z","95.0004565","0.986433900000009","7.53968253968254","0","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","96","2026-09-29T01:46:09.8173619Z","96.0033615","1.002905","6.8359375","0","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","97","2026-09-29T01:46:10.8309004Z","97.0168503","1.0134888","7.30769230769231","0","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","98","2026-09-29T01:46:11.8204146Z","98.0063707","0.989520399999989","5.75396825396825","0","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","99","2026-09-29T01:46:12.8223745Z","99.0084636","1.00209290000001","16.40625","0.194904584195735","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","100","2026-09-29T01:46:13.8210402Z","100.0071295","0.998665899999992","11.5234375","0.195573414492276","12788","0","0","1.97265625","12.515625","7","176","0","0" +"1","allow","101","2026-09-29T01:46:14.8198733Z","101.0058863","0.99875680000001","7.421875","0.195555614740243","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","102","2026-09-29T01:46:15.8322845Z","102.0184203","1.01253399999999","6.92307692307692","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","103","2026-09-29T01:46:16.8316676Z","103.0177388","0.999318500000001","5.859375","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","104","2026-09-29T01:46:17.8283567Z","104.0144184","0.996679600000007","9.5703125","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","105","2026-09-29T01:46:18.8269295Z","105.0129537","0.9985353","8.203125","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","106","2026-09-29T01:46:19.8247283Z","106.0108122","0.997858499999992","4.4921875","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","107","2026-09-29T01:46:20.8212842Z","107.0073287","0.996516500000013","8.13492063492063","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","108","2026-09-29T01:46:21.8223590Z","108.00863","1.00130129999999","12.6953125","0.195058670152532","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","109","2026-09-29T01:46:22.8202555Z","109.0062223","0.997592299999994","9.9609375","0.195783888869232","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","110","2026-09-29T01:46:23.8207912Z","110.0067317","1.0005094","6.0546875","0.195213058468017","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","111","2026-09-29T01:46:24.8331370Z","111.0190754","1.0123437","8.26923076923077","0.192931017400513","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","112","2026-09-29T01:46:25.8336677Z","112.0196395","1.00056410000001","6.25","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","113","2026-09-29T01:46:26.8192507Z","113.005207","0.985567500000002","5.55555555555556","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","114","2026-09-29T01:46:27.8327670Z","114.0187476","1.0135406","5.57692307692308","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","115","2026-09-29T01:46:28.8200416Z","115.0063012","0.987553599999998","9.32539682539683","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","116","2026-09-29T01:46:29.8185217Z","116.0045131","0.998211900000001","6.25","0.195662363872841","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","117","2026-09-29T01:46:30.8304399Z","117.016512","1.01199889999999","5.38461538461539","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","118","2026-09-29T01:46:31.8156181Z","118.0013971","0.9848851","4.96031746031746","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","119","2026-09-29T01:46:32.8282845Z","119.0143369","1.0129398","5.96153846153846","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","120","2026-09-29T01:46:33.8255508Z","120.0117286","0.997391700000009","11.71875","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","121","2026-09-29T01:46:34.8294369Z","121.015502","1.0037734","3.515625","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","122","2026-09-29T01:46:35.8269063Z","122.0128831","0.997381099999998","4.8828125","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","123","2026-09-29T01:46:36.8234781Z","123.0096922","0.996809099999993","13.8671875","0.195937717663293","12788","0.015625","0.195937717663293","1.9140625","12.4921875","5","174","0","0" +"1","allow","124","2026-09-29T01:46:37.8206714Z","124.0067917","0.997099500000004","16.8650793650794","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","125","2026-09-29T01:46:38.8324488Z","125.0185253","1.0117336","13.6538461538462","0.193047359502541","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","126","2026-09-29T01:46:39.8190182Z","126.0053185","0.986793200000008","5.95238095238095","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","127","2026-09-29T01:46:40.8211696Z","127.0071752","1.00185669999999","7.421875","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","128","2026-09-29T01:46:41.8248080Z","128.0108556","1.00368039999999","17.6923076923077","0.194596307748962","12788","0.015625","0.194596307748962","1.9140625","12.4921875","5","174","0","0" +"1","allow","129","2026-09-29T01:46:42.8334151Z","129.0189828","1.00812720000002","5.859375","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","130","2026-09-29T01:46:43.8331380Z","130.019318","1.00033519999999","5.46875","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","131","2026-09-29T01:46:44.8307485Z","131.0167073","0.997389300000009","3.90625","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","132","2026-09-29T01:46:45.8260767Z","132.0120679","0.995360599999998","13.671875","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","133","2026-09-29T01:46:46.8268580Z","133.0129937","1.00092579999998","5.46875","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","134","2026-09-29T01:46:47.8250010Z","134.0112331","0.998239400000017","6.25","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","135","2026-09-29T01:46:48.8290650Z","135.015093","1.00385990000001","7.6171875","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","136","2026-09-29T01:46:49.8147900Z","136.0007566","0.985663599999981","8.73015873015873","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","137","2026-09-29T01:46:50.8141864Z","137.000193","0.999436400000008","5.0880626223092","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","138","2026-09-29T01:46:51.8180800Z","138.0042903","1.00409729999998","5.45808966861598","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","139","2026-09-29T01:46:52.8291956Z","139.0151653","1.01087500000003","6.34615384615385","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","140","2026-09-29T01:46:53.8286877Z","140.01474","0.999574699999982","7.6171875","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","141","2026-09-29T01:46:54.8270411Z","141.0131003","0.998360300000002","5.46875","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","142","2026-09-29T01:46:55.8255229Z","142.0115288","0.998428500000017","4.8828125","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","143","2026-09-29T01:46:56.8301852Z","143.0165374","1.0050086","5.859375","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","144","2026-09-29T01:46:57.8158748Z","144.0019342","0.98539679999999","7.53968253968254","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","145","2026-09-29T01:46:58.8348644Z","145.0204294","1.01849519999999","10.9615384615385","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","146","2026-09-29T01:46:59.8332539Z","146.0193391","0.998909700000013","6.8359375","0.195525681650701","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","147","2026-09-29T01:47:00.8316479Z","147.0176048","0.99826569999999","6.0546875","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","148","2026-09-29T01:47:01.8202996Z","148.0064185","0.988813700000009","5.55555555555556","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","149","2026-09-29T01:47:02.8339705Z","149.0201599","1.01374139999999","26.1538461538462","0.192665012990495","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","150","2026-09-29T01:47:03.8187265Z","150.0047231","0.984563200000025","5.36779324055666","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","151","2026-09-29T01:47:04.8223754Z","151.0083614","1.00363829999998","7.40740740740741","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","152","2026-09-29T01:47:05.8244450Z","152.010502","1.00214060000002","13.4615384615385","0.194895307105606","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","153","2026-09-29T01:47:06.8266915Z","153.0127075","1.0022055","13.28125","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","154","2026-09-29T01:47:07.8144899Z","154.0004334","0.987725899999987","7.73809523809524","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","155","2026-09-29T01:47:08.8238225Z","155.0098752","1.00944179999999","4.42307692307692","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","156","2026-09-29T01:47:09.8227624Z","156.0088227","0.998947500000014","17.8571428571429","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","157","2026-09-29T01:47:10.8228961Z","157.0089168","1.00009409999998","11.9140625","0.195294122823045","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","158","2026-09-29T01:47:11.8212966Z","158.0073173","0.998400500000002","7.8125","0.195625402831829","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","159","2026-09-29T01:47:12.8196967Z","159.0056788","0.998361500000016","9.5703125","0.19563304474381","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","160","2026-09-29T01:47:13.8191323Z","160.0050573","0.999378500000006","8.203125","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","161","2026-09-29T01:47:14.8186654Z","161.0046766","0.999619299999978","5.46875","0.195386883786662","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","162","2026-09-29T01:47:15.8325326Z","162.0184812","1.01380460000001","16.5384615384615","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","163","2026-09-29T01:47:16.8199455Z","163.0059131","0.98743189999999","5.95238095238095","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","164","2026-09-29T01:47:17.8196877Z","164.0056945","0.999781400000018","7.421875","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","165","2026-09-29T01:47:18.8348087Z","165.0203917","1.0146972","6.15384615384615","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","166","2026-09-29T01:47:19.8338782Z","166.0197965","0.999404799999979","9.1796875","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","167","2026-09-29T01:47:20.8315426Z","167.0175181","0.997721600000006","7.2265625","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","168","2026-09-29T01:47:21.8208517Z","168.0069046","0.989386499999995","7.93650793650794","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","169","2026-09-29T01:47:22.8245768Z","169.0105175","1.00361290000001","12.1153846153846","0.194609395714223","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","170","2026-09-29T01:47:23.8232372Z","170.0098903","0.999372800000003","13.0859375","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","171","2026-09-29T01:47:24.8209993Z","171.0069185","0.997028199999988","29.7619047619048","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","172","2026-09-29T01:47:25.8276461Z","172.0136304","1.0067119","39.2307692307692","0.19401032211897","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","173","2026-09-29T01:47:26.8247490Z","173.010752","0.997121600000014","14.84375","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","174","2026-09-29T01:47:27.8209983Z","174.0070007","0.996248699999995","11.1111111111111","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","175","2026-09-29T01:47:28.8229974Z","175.0091385","1.00213780000001","19.921875","0.194895851648343","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","176","2026-09-29T01:47:29.8211499Z","176.0072325","0.99809399999998","18.359375","0.195685476518248","12788","0.015625","0.195685476518248","1.9140625","12.4921875","5","174","0","0" +"1","allow","177","2026-09-29T01:47:30.8230977Z","177.0090351","1.00180260000002","9.9609375","0.194961063187495","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","178","2026-09-29T01:47:31.8229811Z","178.0090167","0.999981599999984","12.109375","0.195316093816129","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","179","2026-09-29T01:47:32.8294482Z","179.0154331","1.00641640000001","6.34615384615385","0.194067286661862","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","180","2026-09-29T01:47:33.8289440Z","180.0149372","0.999504099999996","10.3515625","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","181","2026-09-29T01:47:34.8161081Z","181.0021988","0.987261600000011","7.53968253968254","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","182","2026-09-29T01:47:35.8326884Z","182.0187579","1.01655909999999","6.15384615384615","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","183","2026-09-29T01:47:36.8338873Z","183.0199792","1.0012213","5.078125","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","184","2026-09-29T01:47:37.8309694Z","184.017531","0.997551799999997","7.03125","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","185","2026-09-29T01:47:38.8332897Z","185.0191861","1.00165509999999","6.8359375","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","186","2026-09-29T01:47:39.8186745Z","186.0047864","0.985600300000016","8.53174603174603","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","187","2026-09-29T01:47:40.8212348Z","187.0072906","1.0025042","6.0546875","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","188","2026-09-29T01:47:41.8221188Z","188.008287","1.00099639999999","11.9140625","0.195118084340765","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","189","2026-09-29T01:47:42.8210388Z","189.0069578","0.998670799999985","5.859375","0.195572454907065","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","190","2026-09-29T01:47:43.8237931Z","190.0098011","1.00284330000002","14.0655105973025","0.194758742467537","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","191","2026-09-29T01:47:44.8271717Z","191.0128156","1.00301449999998","6.04288499025341","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","192","2026-09-29T01:47:45.8299433Z","192.0155194","1.00270380000001","5.859375","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","193","2026-09-29T01:47:46.8256884Z","193.0117843","0.9962649","4.6875","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","194","2026-09-29T01:47:47.8146700Z","194.0006878","0.988903499999992","6.34920634920635","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","195","2026-09-29T01:47:48.8146507Z","195.0012628","1.000575","3.90625","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","196","2026-09-29T01:47:49.8284588Z","196.0145017","1.0132389","12.3076923076923","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","197","2026-09-29T01:47:50.8282075Z","197.0141804","0.999678700000004","5.6640625","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","198","2026-09-29T01:47:51.8242772Z","198.0103749","0.996194500000001","9.765625","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","199","2026-09-29T01:47:52.8235682Z","199.0096656","0.999290699999989","7.8125","0.195451133488986","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","200","2026-09-29T01:47:53.8242737Z","200.0102289","1.00056330000001","8.6105675146771","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","201","2026-09-29T01:47:54.8299291Z","201.0159255","1.00569659999999","5.65302144249513","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","202","2026-09-29T01:47:55.8155920Z","202.0016228","0.985697299999998","5.75396825396825","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","203","2026-09-29T01:47:56.8220943Z","203.0080578","1.00643500000001","5.46875","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","204","2026-09-29T01:47:57.8227109Z","204.00869","1.00063220000001","8.984375","0.195189101450061","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","205","2026-09-29T01:47:58.8216006Z","205.0075552","0.998865199999983","6.640625","0.195534392428531","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","206","2026-09-29T01:47:59.8200049Z","206.0060287","0.998473500000017","10.546875","0.195611100344673","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","207","2026-09-29T01:48:00.8198685Z","207.0058383","0.999809599999992","6.8359375","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","208","2026-09-29T01:48:01.8328407Z","208.0188136","1.01297529999999","8.26923076923077","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","209","2026-09-29T01:48:02.8192700Z","209.0054253","0.986611699999997","4.76190476190476","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","210","2026-09-29T01:48:03.8272305Z","210.0132933","1.007868","7.88461538461539","0.193787777764548","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","211","2026-09-29T01:48:04.8258934Z","211.011854","0.998560700000013","5.859375","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","212","2026-09-29T01:48:05.8295225Z","212.0156504","1.0037964","6.8359375","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","213","2026-09-29T01:48:06.8175137Z","213.003628","0.987977599999994","7.73809523809524","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","214","2026-09-29T01:48:07.8211578Z","214.0071267","1.00349869999999","7.421875","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","215","2026-09-29T01:48:08.8336482Z","215.0196221","1.01249540000001","7.11538461538461","0.192902110962676","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","216","2026-09-29T01:48:09.8303697Z","216.0163106","0.996688500000005","8.984375","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","217","2026-09-29T01:48:10.8256570Z","217.0117888","0.99547819999998","8.0078125","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","218","2026-09-29T01:48:11.8261293Z","218.0122064","1.00041760000002","7.2265625","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","219","2026-09-29T01:48:12.8239354Z","219.0098882","0.997681799999981","9.9609375","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","220","2026-09-29T01:48:13.8223455Z","220.0078289","0.997940700000015","24.0079365079365","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","221","2026-09-29T01:48:14.8333622Z","221.0193877","1.01155879999999","31.7307692307692","0.193080718589964","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","222","2026-09-29T01:48:15.8299383Z","222.0161249","0.996737200000013","6.4453125","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","223","2026-09-29T01:48:16.8148219Z","223.0004449","0.984319999999997","6.34920634920635","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","224","2026-09-29T01:48:17.8281210Z","224.0140276","1.0135827","8.07692307692308","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","225","2026-09-29T01:48:18.8170261Z","225.0029409","0.988913300000007","7.14285714285714","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","226","2026-09-29T01:48:19.8272167Z","226.0131012","1.0101603","17.8846153846154","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","227","2026-09-29T01:48:20.8286399Z","227.0146702","1.00156899999999","6.8359375","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","228","2026-09-29T01:48:21.8152506Z","228.0012464","0.986576200000002","9.72222222222222","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","229","2026-09-29T01:48:22.8160328Z","229.0020294","1.00078300000001","5.46875","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","230","2026-09-29T01:48:23.8329863Z","230.0189716","1.01694219999999","4.61538461538461","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","231","2026-09-29T01:48:24.8307212Z","231.0167744","0.997802800000017","5.859375","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","232","2026-09-29T01:48:25.8212288Z","232.0072888","0.990514399999995","8.92857142857143","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","233","2026-09-29T01:48:26.8197566Z","233.0058211","0.998532299999994","7.421875","0.195599581505777","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","234","2026-09-29T01:48:27.8309859Z","234.0169604","1.0111393","5.38461538461539","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","235","2026-09-29T01:48:28.8309729Z","235.016856","0.999895600000002","6.8359375","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","236","2026-09-29T01:48:29.8144054Z","236.0004786","0.98362259999999","6.94444444444444","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","237","2026-09-29T01:48:30.8292408Z","237.0147321","1.01425350000002","6.73076923076923","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","238","2026-09-29T01:48:31.8171263Z","238.0032051","0.988472999999999","5.55555555555556","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","239","2026-09-29T01:48:32.8186443Z","239.0046835","1.0014784","4.4921875","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","240","2026-09-29T01:48:33.8195747Z","240.0057751","1.0010916","8.7890625","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","241","2026-09-29T01:48:34.8187416Z","241.0048749","0.999099799999982","12.109375","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","242","2026-09-29T01:48:35.8325330Z","242.0184863","1.0136114","6.9364161849711","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","243","2026-09-29T01:48:36.8320256Z","243.0176747","0.999188400000008","7.40740740740741","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","244","2026-09-29T01:48:37.8285524Z","244.0146697","0.996994999999998","5.859375","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","245","2026-09-29T01:48:38.8311471Z","245.0171997","1.00253000000001","5.859375","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","246","2026-09-29T01:48:39.8305510Z","246.01654","0.9993403","4.6875","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","247","2026-09-29T01:48:40.8186333Z","247.0045935","0.988053500000007","10.9126984126984","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","248","2026-09-29T01:48:41.8200714Z","248.0060254","1.0014319","8.0078125","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","249","2026-09-29T01:48:42.8188852Z","249.0049999","0.998974500000003","7.6171875","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","250","2026-09-29T01:48:43.8186643Z","250.0046349","0.999634999999984","5.078125","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","251","2026-09-29T01:48:44.8224300Z","251.0085557","1.0039208","6.8359375","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","252","2026-09-29T01:48:45.8263702Z","252.012499","1.0039433","10","0.19454534932401","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","253","2026-09-29T01:48:46.8143460Z","253.0004885","0.987989499999998","7.14285714285714","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","254","2026-09-29T01:48:47.8186504Z","254.0046216","1.00413309999999","4.296875","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","255","2026-09-29T01:48:48.8331569Z","255.0191358","1.01451420000001","5.76923076923077","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","256","2026-09-29T01:48:49.8195418Z","256.0057687","0.986632899999989","7.14285714285714","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","257","2026-09-29T01:48:50.8335931Z","257.0195484","1.01377969999999","8.46153846153846","0.192657734219774","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","258","2026-09-29T01:48:51.8312198Z","258.0172153","0.997666900000013","6.0546875","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","259","2026-09-29T01:48:52.8227253Z","259.0081624","0.990947100000028","6.54761904761905","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","260","2026-09-29T01:48:53.8283915Z","260.0144852","1.00632279999996","10.1923076923077","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","261","2026-09-29T01:48:54.8188160Z","261.0049262","0.990441000000033","3.96825396825397","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","262","2026-09-29T01:48:55.8328400Z","262.0188041","1.01387790000001","7.88461538461539","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","263","2026-09-29T01:48:56.8212973Z","263.007401","0.988596900000005","5.95238095238095","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","264","2026-09-29T01:48:57.8249242Z","264.0110643","1.00366329999997","9.03846153846154","0.194599623200336","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","265","2026-09-29T01:48:58.8246580Z","265.0107699","0.999705600000027","13.0859375","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","266","2026-09-29T01:48:59.8268963Z","266.0131832","1.0024133","6.4453125","0","12788","0","0","1.9140625","12.4921875","5","174","0","0" +"1","allow","267","2026-09-29T01:49:00.8174500Z","267.0034315","0.990248299999962","8.53174603174603","0","12788","0","0","1.9765625","12.66796875","7","174","0","0" +"1","allow","268","2026-09-29T01:49:01.8321648Z","268.0182","1.0147685","6.34615384615385","0","12788","0","0","1.9765625","12.66796875","7","174","0","0" +"1","allow","269","2026-09-29T01:49:02.8327955Z","269.0185213","1.0003213","5.859375","0","12788","0","0","1.9765625","12.66796875","7","174","0","0" +"1","allow","270","2026-09-29T01:49:03.8313536Z","270.0172822","0.998760900000036","7.421875","0","12788","0","0","1.9765625","12.66796875","7","174","0","0" +"1","allow","271","2026-09-29T01:49:04.8145560Z","271.0006674","0.983385199999987","5.75396825396825","0","12788","0","0","1.9765625","12.66796875","7","174","0","0" +"1","allow","272","2026-09-29T01:49:05.8166316Z","272.0025629","1.00189549999999","6.640625","0","12788","0","0","1.9765625","12.66796875","7","174","0","0" +"1","allow","273","2026-09-29T01:49:06.8332859Z","273.0192409","1.01667800000001","7.69230769230769","0","12788","0","0","1.9765625","12.66796875","7","174","0","0" +"1","allow","274","2026-09-29T01:49:07.8228984Z","274.0090105","0.989769599999988","7.93650793650794","0","12788","0","0","1.9765625","12.66796875","7","174","0","0" +"1","allow","275","2026-09-29T01:49:08.8253788Z","275.0115613","1.00255079999999","13.2692307692308","0.194815564458181","12788","0","0","1.9765625","12.66796875","7","174","0","0" +"1","allow","276","2026-09-29T01:49:09.8286774Z","276.0146912","1.00312990000003","7.03125","0","12788","0","0","1.9765625","12.66796875","7","174","0","0" +"1","allow","277","2026-09-29T01:49:10.8245010Z","277.010686","0.995994799999949","8.0078125","0","12788","0","0","1.9765625","12.66796875","7","174","0","0" +"1","allow","278","2026-09-29T01:49:11.8288228Z","278.0148034","1.00411740000004","10.15625","0","12788","0","0","1.9765625","12.66796875","7","174","0","0" +"1","allow","279","2026-09-29T01:49:12.8274579Z","279.013405","0.998601599999972","8.59375","0","12788","0","0","1.9765625","12.66796875","7","174","0","0" +"1","allow","280","2026-09-29T01:49:13.8247533Z","280.0108657","0.997460700000033","13.8671875","0","12788","0","0","1.9765625","12.66796875","7","174","0","0" +"1","allow","281","2026-09-29T01:49:14.8322237Z","281.0181733","1.00730759999999","4.4921875","0","12788","0","0","1.9765625","12.66796875","7","174","0","0" +"1","allow","282","2026-09-29T01:49:15.8338172Z","282.0198837","1.00171039999998","5.46875","0","12788","0","0","1.9765625","12.66796875","7","174","0","0" +"1","allow","283","2026-09-29T01:49:16.8337936Z","283.0198197","0.999935999999991","15.0390625","0","12788","0","0","1.9765625","12.66796875","7","174","0","0" +"1","allow","284","2026-09-29T01:49:17.8297075Z","284.0156187","0.995799000000034","36.5234375","0","12788","0","0","1.9765625","12.66796875","7","174","0","0" +"1","allow","285","2026-09-29T01:49:18.8237835Z","285.0098164","0.994197699999972","25","0","12788","0","0","1.9765625","12.66796875","7","174","0","0" +"1","allow","286","2026-09-29T01:49:19.8228678Z","286.0088788","0.999062400000014","19.8412698412698","0","12788","0","0","1.9765625","12.66796875","7","174","0","0" +"1","allow","287","2026-09-29T01:49:20.8274287Z","287.0134404","1.00456159999999","11.3461538461538","0.194425608145884","12788","0","0","1.9765625","12.66796875","7","174","0","0" +"1","allow","288","2026-09-29T01:49:21.8255179Z","288.0115905","0.998150100000032","7.8125","0","12788","0","0","1.9765625","12.66796875","7","174","0","0" +"1","allow","289","2026-09-29T01:49:22.8147839Z","289.000832","0.989241499999991","6.74603174603175","0","12788","0","0","1.9765625","12.66796875","7","174","0","0" +"1","allow","290","2026-09-29T01:49:23.8201370Z","290.0063005","1.00546850000001","6.25","0","12788","0","0","1.9765625","12.66796875","7","174","0","0" +"1","allow","291","2026-09-29T01:49:24.8225791Z","291.0085932","1.0022927","7.03125","0.194865731337762","12788","0","0","1.9765625","12.66796875","7","174","0","0" +"1","allow","292","2026-09-29T01:49:25.8216671Z","292.007642","0.999048799999969","11.9140625","0.195498458133383","12788","0","0","1.9765625","12.66796875","7","174","0","0" +"1","allow","293","2026-09-29T01:49:26.8239635Z","293.0101238","1.0024818","8.46153846153846","0.194828973453683","12788","0","0","1.9765625","12.66796875","7","174","0","0" +"1","allow","294","2026-09-29T01:49:27.8260835Z","294.0123006","1.00217680000003","9.375","0","12788","0","0","1.9765625","12.66796875","7","174","0","0" +"1","allow","295","2026-09-29T01:49:28.8244943Z","295.0110506","0.998749999999973","10.546875","0","12788","0","0","1.9765625","12.66796875","7","174","0","0" +"1","allow","296","2026-09-29T01:49:29.8274681Z","296.0135635","1.0025129","6.8359375","0","12788","0","0","1.9765625","12.66796875","7","174","0","0" +"1","allow","297","2026-09-29T01:49:30.8189353Z","297.0050425","0.991479000000027","7.93650793650794","0","12788","0","0","1.9765625","12.66796875","7","174","0","0" +"1","allow","298","2026-09-29T01:49:31.8241303Z","298.010117","1.00507449999998","14.0384615384615","0.194326390730244","12788","0","0","1.9765625","12.66796875","7","174","0","0" +"1","allow","299","2026-09-29T01:49:32.8246484Z","299.010722","1.00060500000001","8.59375","0","12788","0","0","1.9765625","12.66796875","7","174","0","0" +"1","allow","300","2026-09-29T01:49:33.8289545Z","300.0151189","1.00439690000002","5.6640625","0","12788","0","0","1.9765625","12.66796875","7","174","0","0" diff --git a/docs/benchmarks/2026-09-29-v0.2.0/results/run-1-baseline.csv b/docs/benchmarks/2026-09-29-v0.2.0/results/run-1-baseline.csv new file mode 100644 index 0000000..c52aacc --- /dev/null +++ b/docs/benchmarks/2026-09-29-v0.2.0/results/run-1-baseline.csv @@ -0,0 +1,301 @@ +"run","scenario","sample","utc","elapsed_s","interval_s","system_cpu_pct","collector_cpu_pct","pid","process_cpu_s","cpu_pct","private_mib","working_set_mib","threads","handles","io_read_bytes_s","io_write_bytes_s" +"1","baseline","1","2026-09-29T01:39:01.6594568Z","1.0082794","1.0070755","6.92307692307692","0","","","","","","","","","" +"1","baseline","2","2026-09-29T01:39:02.6522932Z","2.0055282","0.9972488","10.3515625","0","","","","","","","","","" +"1","baseline","3","2026-09-29T01:39:03.6552279Z","3.0078493","1.0023211","9.1796875","0","","","","","","","","","" +"1","baseline","4","2026-09-29T01:39:04.6574935Z","4.0107926","1.0029433","5.6640625","0","","","","","","","","","" +"1","baseline","5","2026-09-29T01:39:05.6568503Z","5.0097384","0.9989458","8.984375","0","","","","","","","","","" +"1","baseline","6","2026-09-29T01:39:06.6564909Z","6.0098175","1.0000791","7.2265625","0","","","","","","","","","" +"1","baseline","7","2026-09-29T01:39:07.6715114Z","7.0248178","1.0150003","5.19230769230769","0","","","","","","","","","" +"1","baseline","8","2026-09-29T01:39:08.6528507Z","8.0061119","0.9812941","5.15873015873016","0","","","","","","","","","" +"1","baseline","9","2026-09-29T01:39:09.6502458Z","9.0034491","0.997337199999999","14.0873015873016","0","","","","","","","","","" +"1","baseline","10","2026-09-29T01:39:10.6561449Z","10.0095025","1.0060534","24.6153846153846","0","","","","","","","","","" +"1","baseline","11","2026-09-29T01:39:11.6487899Z","11.0020668","0.9925643","35.3174603174603","0","","","","","","","","","" +"1","baseline","12","2026-09-29T01:39:12.6472993Z","12.0005746","0.998507800000001","15.8203125","0","","","","","","","","","" +"1","baseline","13","2026-09-29T01:39:13.6585331Z","13.0117713","1.0111967","12.8846153846154","0","","","","","","","","","" +"1","baseline","14","2026-09-29T01:39:14.6599908Z","14.0133746","1.0016033","7.8125","0","","","","","","","","","" +"1","baseline","15","2026-09-29T01:39:15.6472683Z","15.0005802","0.987205600000001","4.36507936507936","0","","","","","","","","","" +"1","baseline","16","2026-09-29T01:39:16.6593881Z","16.0126579","1.0120777","4.42307692307692","0","","","","","","","","","" +"1","baseline","17","2026-09-29T01:39:17.6565448Z","17.0099082","0.997250299999997","7.421875","1.37095722107078","","","","","","","","","" +"1","baseline","18","2026-09-29T01:39:18.6615440Z","18.0143141","1.0044059","7.8125","0","","","","","","","","","" +"1","baseline","19","2026-09-29T01:39:19.6478395Z","19.0011373","0.9868232","6.54761904761905","0","","","","","","","","","" +"1","baseline","20","2026-09-29T01:39:20.6482687Z","20.0014386","1.0003013","7.03125","0","","","","","","","","","" +"1","baseline","21","2026-09-29T01:39:21.6525888Z","21.0057626","1.004324","11.9230769230769","0","","","","","","","","","" +"1","baseline","22","2026-09-29T01:39:22.6514144Z","22.0047037","0.9989411","15.6746031746032","0","","","","","","","","","" +"1","baseline","23","2026-09-29T01:39:23.6527574Z","23.0059817","1.001278","12.3314065510597","0.195063209218619","","","","","","","","","" +"1","baseline","24","2026-09-29T01:39:24.6622677Z","24.0155167","1.009535","7.01754385964912","0","","","","","","","","","" +"1","baseline","25","2026-09-29T01:39:25.6572556Z","25.01045","0.9949333","7.421875","0","","","","","","","","","" +"1","baseline","26","2026-09-29T01:39:26.6572477Z","26.0106508","1.0002008","6.25","0","","","","","","","","","" +"1","baseline","27","2026-09-29T01:39:27.6580745Z","27.011264","1.0006132","6.640625","0","","","","","","","","","" +"1","baseline","28","2026-09-29T01:39:28.6543034Z","28.0074994","0.996235400000003","4.6875","0","","","","","","","","","" +"1","baseline","29","2026-09-29T01:39:29.6525914Z","29.0057632","0.998263799999997","12.109375","0","","","","","","","","","" +"1","baseline","30","2026-09-29T01:39:30.6541161Z","30.0074088","1.0016456","6.8359375","0","","","","","","","","","" +"1","baseline","31","2026-09-29T01:39:31.6516445Z","31.004555","0.9971462","8.73015873015873","0","","","","","","","","","" +"1","baseline","32","2026-09-29T01:39:32.6486186Z","32.0017803","0.9972253","11.1328125","0.195855941480827","","","","","","","","","" +"1","baseline","33","2026-09-29T01:39:33.6508200Z","33.0040629","1.0022826","14.453125","0","","","","","","","","","" +"1","baseline","34","2026-09-29T01:39:34.6531946Z","34.0064233","1.0023604","10.1923076923077","0","","","","","","","","","" +"1","baseline","35","2026-09-29T01:39:35.6509344Z","35.004295","0.997871699999997","11.5079365079365","0","","","","","","","","","" +"1","baseline","36","2026-09-29T01:39:36.6542420Z","36.0074512","1.0031562","12.3076923076923","0.194697994190735","","","","","","","","","" +"1","baseline","37","2026-09-29T01:39:37.6556256Z","37.0088639","1.0014127","6.4453125","0","","","","","","","","","" +"1","baseline","38","2026-09-29T01:39:38.6606863Z","38.0140038","1.0051399","4.296875","0","","","","","","","","","" +"1","baseline","39","2026-09-29T01:39:39.6598125Z","39.0131174","0.999113600000001","11.71875","0","","","","","","","","","" +"1","baseline","40","2026-09-29T01:39:40.6513400Z","40.0048309","0.991713499999996","9.43025540275049","0.196944480437143","","","","","","","","","" +"1","baseline","41","2026-09-29T01:39:41.6492209Z","41.002405","0.997574100000001","13.2149901380671","0","","","","","","","","","" +"1","baseline","42","2026-09-29T01:39:42.6543913Z","42.0077284","1.0053234","12.6923076923077","0","","","","","","","","","" +"1","baseline","43","2026-09-29T01:39:43.6547835Z","43.0081417","1.0004133","9.375","0","","","","","","","","","" +"1","baseline","44","2026-09-29T01:39:44.6620303Z","44.0153683","1.0072266","8.7890625","0","","","","","","","","","" +"1","baseline","45","2026-09-29T01:39:45.6472738Z","45.0004606","0.985092299999998","6.15079365079365","0","","","","","","","","","" +"1","baseline","46","2026-09-29T01:39:46.6560170Z","46.0093398","1.0088792","8.46153846153846","0","","","","","","","","","" +"1","baseline","47","2026-09-29T01:39:47.6580363Z","47.0113663","1.0020265","22.265625","0","","","","","","","","","" +"1","baseline","48","2026-09-29T01:39:48.6592975Z","48.0120891","1.0007228","7.421875","0","","","","","","","","","" +"1","baseline","49","2026-09-29T01:39:49.6498341Z","49.0031095","0.991020400000004","8.13492063492063","0","","","","","","","","","" +"1","baseline","50","2026-09-29T01:39:50.6493221Z","50.0027789","0.999669399999995","7.2265625","0","","","","","","","","","" +"1","baseline","51","2026-09-29T01:39:51.6523652Z","51.0055365","1.0027576","8.84615384615385","0","","","","","","","","","" +"1","baseline","52","2026-09-29T01:39:52.6565923Z","52.0101232","1.0045867","10.9375","0","","","","","","","","","" +"1","baseline","53","2026-09-29T01:39:53.6577898Z","53.0110461","1.0009229","7.03125","0","","","","","","","","","" +"1","baseline","54","2026-09-29T01:39:54.6619713Z","54.0152465","1.0042004","6.4453125","0","","","","","","","","","" +"1","baseline","55","2026-09-29T01:39:55.6620079Z","55.0154029","1.0001564","8.3984375","0","","","","","","","","","" +"1","baseline","56","2026-09-29T01:39:56.6613940Z","56.0147422","0.999339300000003","6.640625","0","","","","","","","","","" +"1","baseline","57","2026-09-29T01:39:57.6604653Z","57.0132261","0.998483899999997","7.2265625","0","","","","","","","","","" +"1","baseline","58","2026-09-29T01:39:58.6510058Z","58.0041926","0.990966499999999","35.9126984126984","0","","","","","","","","","" +"1","baseline","59","2026-09-29T01:39:59.6494152Z","59.0027287","0.998536100000003","27.5390625","0","","","","","","","","","" +"1","baseline","60","2026-09-29T01:40:00.6608508Z","60.0143449","1.0116162","17.6923076923077","0","","","","","","","","","" +"1","baseline","61","2026-09-29T01:40:01.6467544Z","61.0000578","0.985712900000003","8.73015873015873","0","","","","","","","","","" +"1","baseline","62","2026-09-29T01:40:02.6513033Z","62.0045192","1.0044614","6.0546875","0","","","","","","","","","" +"1","baseline","63","2026-09-29T01:40:03.6571614Z","63.0105917","1.0060725","15.5769230769231","0.194133623570866","","","","","","","","","" +"1","baseline","64","2026-09-29T01:40:04.6516104Z","64.0049428","0.994351099999996","12.37721021611","0.196422068623448","","","","","","","","","" +"1","baseline","65","2026-09-29T01:40:05.6559392Z","65.0094187","1.0044759","8.3495145631068","0","","","","","","","","","" +"1","baseline","66","2026-09-29T01:40:06.6576867Z","66.0110198","1.0016011","6.4453125","0","","","","","","","","","" +"1","baseline","67","2026-09-29T01:40:07.6583174Z","67.0116935","1.00067369999999","5.2734375","0","","","","","","","","","" +"1","baseline","68","2026-09-29T01:40:08.6591696Z","68.0126318","1.0009383","7.8125","0","","","","","","","","","" +"1","baseline","69","2026-09-29T01:40:09.6562882Z","69.0095146","0.996882800000009","4.6875","0","","","","","","","","","" +"1","baseline","70","2026-09-29T01:40:10.6589784Z","70.0121772","1.00266259999999","5.68627450980392","0","","","","","","","","","" +"1","baseline","71","2026-09-29T01:40:11.6570745Z","71.0105536","0.998376399999998","7.19844357976654","0","","","","","","","","","" +"1","baseline","72","2026-09-29T01:40:12.6615922Z","72.0149097","1.0043561","6.0546875","0","","","","","","","","","" +"1","baseline","73","2026-09-29T01:40:13.6622878Z","73.0156202","1.00071050000001","4.4921875","0","","","","","","","","","" +"1","baseline","74","2026-09-29T01:40:14.6499791Z","74.0032408","0.9876206","8.53174603174603","0","","","","","","","","","" +"1","baseline","75","2026-09-29T01:40:15.6524439Z","75.0056244","1.0023836","12.1153846153846","0","","","","","","","","","" +"1","baseline","76","2026-09-29T01:40:16.6567665Z","76.0100867","1.0044623","7.421875","0","","","","","","","","","" +"1","baseline","77","2026-09-29T01:40:17.6597398Z","77.0131428","1.00305609999999","4.8828125","0","","","","","","","","","" +"1","baseline","78","2026-09-29T01:40:18.6487646Z","78.0016418","0.988499000000004","6.15079365079365","0","","","","","","","","","" +"1","baseline","79","2026-09-29T01:40:19.6510197Z","79.0044529","1.00281109999999","9.765625","0","","","","","","","","","" +"1","baseline","80","2026-09-29T01:40:20.6483263Z","80.001858","0.997405100000009","12.890625","0","","","","","","","","","" +"1","baseline","81","2026-09-29T01:40:21.6490378Z","81.0021981","1.0003401","6.640625","0","","","","","","","","","" +"1","baseline","82","2026-09-29T01:40:22.6487136Z","82.0018878","0.99968969999999","5.67514677103718","0","","","","","","","","","" +"1","baseline","83","2026-09-29T01:40:23.6490185Z","83.0022553","1.00036750000001","6.82261208576998","0","","","","","","","","","" +"1","baseline","84","2026-09-29T01:40:24.6505677Z","84.003814","1.00155869999999","12.5","0","","","","","","","","","" +"1","baseline","85","2026-09-29T01:40:25.6497634Z","85.0030412","0.999227200000007","9.5703125","0","","","","","","","","","" +"1","baseline","86","2026-09-29T01:40:26.6472840Z","86.0007865","0.997745299999991","11.9140625","0","","","","","","","","","" +"1","baseline","87","2026-09-29T01:40:27.6613162Z","87.0145509","1.0137644","5.96153846153846","0","","","","","","","","","" +"1","baseline","88","2026-09-29T01:40:28.6614175Z","88.0147895","1.0002386","5.6640625","0","","","","","","","","","" +"1","baseline","89","2026-09-29T01:40:29.6482561Z","89.0013867","0.986597200000006","4.76190476190476","0","","","","","","","","","" +"1","baseline","90","2026-09-29T01:40:30.6522424Z","90.0054542","1.0040675","11.7307692307692","0.38904256934917","","","","","","","","","" +"1","baseline","91","2026-09-29T01:40:31.6515333Z","91.0047235","0.999269299999995","9.46745562130178","0.195455319201742","","","","","","","","","" +"1","baseline","92","2026-09-29T01:40:32.6517717Z","92.0050089","1.0002854","10.6382978723404","0","","","","","","","","","" +"1","baseline","93","2026-09-29T01:40:33.6532003Z","93.006435","1.0014261","9.98043052837573","0","","","","","","","","","" +"1","baseline","94","2026-09-29T01:40:34.6554018Z","94.0086977","1.0022627","7.21247563352826","0","","","","","","","","","" +"1","baseline","95","2026-09-29T01:40:35.6553473Z","95.0085562","0.999858500000002","7.03125","0","","","","","","","","","" +"1","baseline","96","2026-09-29T01:40:36.6611212Z","96.0143328","1.00577659999999","6.8359375","0","","","","","","","","","" +"1","baseline","97","2026-09-29T01:40:37.6475318Z","97.001004","0.986671200000004","7.14285714285714","0","","","","","","","","","" +"1","baseline","98","2026-09-29T01:40:38.6532012Z","98.0064497","1.0054457","6.73076923076923","0","","","","","","","","","" +"1","baseline","99","2026-09-29T01:40:39.6521449Z","99.0054653","0.999015600000007","7.03125","0","","","","","","","","","" +"1","baseline","100","2026-09-29T01:40:40.6549013Z","100.0080644","1.0025991","11.1328125","0","","","","","","","","","" +"1","baseline","101","2026-09-29T01:40:41.6569690Z","101.0102805","1.0022161","5.6640625","0","","","","","","","","","" +"1","baseline","102","2026-09-29T01:40:42.6550222Z","102.0083951","0.998114600000008","5.078125","0","","","","","","","","","" +"1","baseline","103","2026-09-29T01:40:43.6522454Z","103.0054574","0.997062299999996","6.25","0","","","","","","","","","" +"1","baseline","104","2026-09-29T01:40:44.6515128Z","104.0047789","0.999321499999994","22.4409448818898","0.195445109506802","","","","","","","","","" +"1","baseline","105","2026-09-29T01:40:45.6554636Z","105.0088715","1.00409260000001","6.0077519379845","0","","","","","","","","","" +"1","baseline","106","2026-09-29T01:40:46.6512032Z","106.0043779","0.995506399999996","9.52380952380952","0","","","","","","","","","" +"1","baseline","107","2026-09-29T01:40:47.6511629Z","107.0044642","1.00008630000001","10.546875","0.19529564598575","","","","","","","","","" +"1","baseline","108","2026-09-29T01:40:48.6582158Z","108.0114028","1.0069386","9.80769230769231","0.19396664305053","","","","","","","","","" +"1","baseline","109","2026-09-29T01:40:49.6585333Z","109.0118613","1.00045849999999","5.2734375","0","","","","","","","","","" +"1","baseline","110","2026-09-29T01:40:50.6613275Z","110.0145709","1.0027096","6.640625","0","","","","","","","","","" +"1","baseline","111","2026-09-29T01:40:51.6463933Z","110.9995062","0.984935300000004","9.92063492063492","0","","","","","","","","","" +"1","baseline","112","2026-09-29T01:40:52.6596368Z","112.0128171","1.01331089999999","11.7307692307692","0","","","","","","","","","" +"1","baseline","113","2026-09-29T01:40:53.6572064Z","113.0105197","0.997702599999997","8.203125","0","","","","","","","","","" +"1","baseline","114","2026-09-29T01:40:54.6571288Z","114.0103047","0.999785000000003","15.4296875","0","","","","","","","","","" +"1","baseline","115","2026-09-29T01:40:55.6522484Z","115.005455","0.995150300000006","27.34375","0","","","","","","","","","" +"1","baseline","116","2026-09-29T01:40:56.6555121Z","116.0086693","1.0032143","5.859375","0","","","","","","","","","" +"1","baseline","117","2026-09-29T01:40:57.6583842Z","117.0116613","1.00299200000001","8.7890625","0","","","","","","","","","" +"1","baseline","118","2026-09-29T01:40:58.6536252Z","118.0068049","0.995143599999992","13.671875","0","","","","","","","","","" +"1","baseline","119","2026-09-29T01:40:59.6496137Z","119.0029158","0.996110900000005","16.6666666666667","0","","","","","","","","","" +"1","baseline","120","2026-09-29T01:41:00.6596089Z","120.0125099","1.0095941","12.3076923076923","0","","","","","","","","","" +"1","baseline","121","2026-09-29T01:41:01.6587969Z","121.0120616","0.999551699999998","13.28125","0","","","","","","","","","" +"1","baseline","122","2026-09-29T01:41:02.6570458Z","122.0104056","0.998344000000003","6.25","0","","","","","","","","","" +"1","baseline","123","2026-09-29T01:41:03.6548089Z","123.007975","0.997569399999989","8.7890625","0","","","","","","","","","" +"1","baseline","124","2026-09-29T01:41:04.6602457Z","124.0134659","1.00549090000001","9.9609375","0","","","","","","","","","" +"1","baseline","125","2026-09-29T01:41:05.6501737Z","125.0028657","0.989399800000001","6.16302186878728","0","","","","","","","","","" +"1","baseline","126","2026-09-29T01:41:06.6501694Z","126.003443","1.00057729999999","6.4327485380117","0","","","","","","","","","" +"1","baseline","127","2026-09-29T01:41:07.6490096Z","127.0022608","0.998817799999998","13.28125","0","","","","","","","","","" +"1","baseline","128","2026-09-29T01:41:08.6485971Z","128.0018507","0.999589900000018","5.6640625","0","","","","","","","","","" +"1","baseline","129","2026-09-29T01:41:09.6532552Z","129.0065787","1.004728","7.69230769230769","0","","","","","","","","","" +"1","baseline","130","2026-09-29T01:41:10.6527784Z","130.0059916","0.999412899999982","8.7890625","0","","","","","","","","","" +"1","baseline","131","2026-09-29T01:41:11.6517565Z","131.0049338","0.998942200000016","13.1115459882583","0.195519320337049","","","","","","","","","" +"1","baseline","132","2026-09-29T01:41:12.6538753Z","132.007145","1.00221119999998","9.55165692007797","0","","","","","","","","","" +"1","baseline","133","2026-09-29T01:41:13.6528944Z","133.006036","0.998891000000015","12.5","0","","","","","","","","","" +"1","baseline","134","2026-09-29T01:41:14.6496318Z","134.0034348","0.997398800000013","10.9126984126984","0","","","","","","","","","" +"1","baseline","135","2026-09-29T01:41:15.6523683Z","135.0050488","1.00161399999999","12.5","0","","","","","","","","","" +"1","baseline","136","2026-09-29T01:41:16.6517503Z","136.0050169","0.99996809999999","8.984375","0","","","","","","","","","" +"1","baseline","137","2026-09-29T01:41:17.6520773Z","137.005302","1.00028510000001","15.4296875","0","","","","","","","","","" +"1","baseline","138","2026-09-29T01:41:18.6515673Z","138.0047979","0.999495899999999","16.8958742632613","0.195411006688472","","","","","","","","","" +"1","baseline","139","2026-09-29T01:41:19.6521198Z","139.0052817","1.00048379999998","10.873786407767","0","","","","","","","","","" +"1","baseline","140","2026-09-29T01:41:20.6525240Z","140.0053901","1.00010840000002","8.984375","0","","","","","","","","","" +"1","baseline","141","2026-09-29T01:41:21.6548301Z","141.0081012","1.0027111","7.2265625","0","","","","","","","","","" +"1","baseline","142","2026-09-29T01:41:22.6538678Z","142.0071556","0.999054400000006","6.640625","0","","","","","","","","","" +"1","baseline","143","2026-09-29T01:41:23.6548147Z","143.0080595","1.0009039","8.43137254901961","0","","","","","","","","","" +"1","baseline","144","2026-09-29T01:41:24.6497618Z","144.0029938","0.994934299999983","8.71287128712871","0","","","","","","","","","" +"1","baseline","145","2026-09-29T01:41:25.6512896Z","145.004585","1.00159120000001","13.8401559454191","0","","","","","","","","","" +"1","baseline","146","2026-09-29T01:41:26.6586552Z","146.011823","1.007238","9.61538461538461","0.193908986753875","","","","","","","","","" +"1","baseline","147","2026-09-29T01:41:27.6561905Z","147.009496","0.997672999999992","8.3984375","0","","","","","","","","","" +"1","baseline","148","2026-09-29T01:41:28.6518873Z","148.0050567","0.995560699999999","6.0665362035225","0","","","","","","","","","" +"1","baseline","149","2026-09-29T01:41:29.6497425Z","149.0032859","0.998229199999997","9.10891089108911","0","","","","","","","","","" +"1","baseline","150","2026-09-29T01:41:30.6497557Z","150.0031132","0.999827300000021","14.453125","0","","","","","","","","","" +"1","baseline","151","2026-09-29T01:41:31.6484509Z","151.001755","0.998641800000001","9.9609375","0","","","","","","","","","" +"1","baseline","152","2026-09-29T01:41:32.6489763Z","152.0023464","1.00059139999999","6.8359375","0","","","","","","","","","" +"1","baseline","153","2026-09-29T01:41:33.6512746Z","153.0045683","1.0022219","12.6953125","0","","","","","","","","","" +"1","baseline","154","2026-09-29T01:41:34.6526010Z","154.0058703","1.00130200000001","7.30769230769231","0.195058533789005","","","","","","","","","" +"1","baseline","155","2026-09-29T01:41:35.6545367Z","155.0077025","1.0018322","8.59375","0","","","","","","","","","" +"1","baseline","156","2026-09-29T01:41:36.6538527Z","156.0070022","0.999299699999995","5.2734375","0","","","","","","","","","" +"1","baseline","157","2026-09-29T01:41:37.6503606Z","157.0035368","0.996534600000018","8.53174603174603","0","","","","","","","","","" +"1","baseline","158","2026-09-29T01:41:38.6520343Z","158.0054106","1.0018738","9.03846153846154","0","","","","","","","","","" +"1","baseline","159","2026-09-29T01:41:39.6522437Z","159.005651","1.0002404","10.15625","0","","","","","","","","","" +"1","baseline","160","2026-09-29T01:41:40.6489572Z","160.0022035","0.996552499999979","10.1190476190476","0","","","","","","","","","" +"1","baseline","161","2026-09-29T01:41:41.6620879Z","161.0149325","1.01272900000001","5.57692307692308","0","","","","","","","","","" +"1","baseline","162","2026-09-29T01:41:42.6610630Z","162.0142803","0.99934780000001","6.4453125","0","","","","","","","","","" +"1","baseline","163","2026-09-29T01:41:43.6607227Z","163.0138871","0.999606800000009","7.04500978473581","0","","","","","","","","","" +"1","baseline","164","2026-09-29T01:41:44.6582941Z","164.0114638","0.997576699999996","5.0880626223092","0","","","","","","","","","" +"1","baseline","165","2026-09-29T01:41:45.6475988Z","165.0009313","0.989467499999989","6.52173913043478","0","","","","","","","","","" +"1","baseline","166","2026-09-29T01:41:46.6487695Z","166.0021296","1.0011983","7.6171875","0","","","","","","","","","" +"1","baseline","167","2026-09-29T01:41:47.6506349Z","167.0039506","1.00182100000001","8.7890625","0","","","","","","","","","" +"1","baseline","168","2026-09-29T01:41:48.6503428Z","168.003628","0.999677399999996","8.7890625","0","","","","","","","","","" +"1","baseline","169","2026-09-29T01:41:49.6516008Z","169.0048549","1.00122690000001","8.94941634241245","0","","","","","","","","","" +"1","baseline","170","2026-09-29T01:41:50.6520717Z","170.005274","1.00041909999999","9.26640926640927","0.195230678822508","","","","","","","","","" +"1","baseline","171","2026-09-29T01:41:51.6563729Z","171.0096626","1.0043886","5.078125","0","","","","","","","","","" +"1","baseline","172","2026-09-29T01:41:52.6546390Z","172.0078706","0.998208000000005","4.4921875","0","","","","","","","","","" +"1","baseline","173","2026-09-29T01:41:53.6537274Z","173.0061377","0.998267099999993","6.0546875","0","","","","","","","","","" +"1","baseline","174","2026-09-29T01:41:54.6550806Z","174.0084113","1.00227360000002","8.7890625","0","","","","","","","","","" +"1","baseline","175","2026-09-29T01:41:55.6560127Z","175.0092737","1.00086239999999","10.546875","0","","","","","","","","","" +"1","baseline","176","2026-09-29T01:41:56.6583826Z","176.0115602","1.0022865","5.078125","0","","","","","","","","","" +"1","baseline","177","2026-09-29T01:41:57.6567157Z","177.0101448","0.998584600000015","7.8125","0","","","","","","","","","" +"1","baseline","178","2026-09-29T01:41:58.6551835Z","178.0084358","0.998290999999995","5.46875","0","","","","","","","","","" +"1","baseline","179","2026-09-29T01:41:59.6611942Z","179.0145313","1.00609549999999","8.984375","0","","","","","","","","","" +"1","baseline","180","2026-09-29T01:42:00.6590522Z","180.0124082","0.997876899999994","6.0546875","0","","","","","","","","","" +"1","baseline","181","2026-09-29T01:42:01.6507173Z","181.0034925","0.991084300000011","6.15079365079365","0","","","","","","","","","" +"1","baseline","182","2026-09-29T01:42:02.6551563Z","182.0084432","1.00495069999999","6.15384615384615","0","","","","","","","","","" +"1","baseline","183","2026-09-29T01:42:03.6568879Z","183.0097856","1.0013424","6.25","0","","","","","","","","","" +"1","baseline","184","2026-09-29T01:42:04.6610699Z","184.0145478","1.00476220000002","5.859375","0","","","","","","","","","" +"1","baseline","185","2026-09-29T01:42:05.6599012Z","185.0129432","0.998395399999993","10.546875","0","","","","","","","","","" +"1","baseline","186","2026-09-29T01:42:06.6568691Z","186.0101691","0.997225899999989","5.078125","0","","","","","","","","","" +"1","baseline","187","2026-09-29T01:42:07.6571443Z","187.0105462","1.00037710000001","6.4453125","0","","","","","","","","","" +"1","baseline","188","2026-09-29T01:42:08.6592045Z","188.0124809","1.00193469999999","4.6875","0","","","","","","","","","" +"1","baseline","189","2026-09-29T01:42:09.6597015Z","189.0128648","1.0003839","5.078125","0","","","","","","","","","" +"1","baseline","190","2026-09-29T01:42:10.6575922Z","190.0109627","0.998097900000005","7.03125","0","","","","","","","","","" +"1","baseline","191","2026-09-29T01:42:11.6588979Z","191.0121577","1.001195","6.8359375","0","","","","","","","","","" +"1","baseline","192","2026-09-29T01:42:12.6586037Z","192.0118596","0.999701899999991","6.640625","0","","","","","","","","","" +"1","baseline","193","2026-09-29T01:42:13.6488488Z","193.0020093","0.990149700000018","3.76984126984127","0","","","","","","","","","" +"1","baseline","194","2026-09-29T01:42:14.6508246Z","194.0040038","1.0019945","7.03125","0","","","","","","","","","" +"1","baseline","195","2026-09-29T01:42:15.6543324Z","195.0075374","1.0035336","10.1923076923077","0","","","","","","","","","" +"1","baseline","196","2026-09-29T01:42:16.6538837Z","196.0066577","0.999120300000016","5.859375","0","","","","","","","","","" +"1","baseline","197","2026-09-29T01:42:17.6570779Z","197.0103691","1.00371139999999","6.25","0","","","","","","","","","" +"1","baseline","198","2026-09-29T01:42:18.6564830Z","198.0096432","0.999274100000008","6.0546875","0","","","","","","","","","" +"1","baseline","199","2026-09-29T01:42:19.6533798Z","199.0066083","0.996965099999983","21.6796875","0","","","","","","","","","" +"1","baseline","200","2026-09-29T01:42:20.6512067Z","200.0044352","0.997826900000007","40.2777777777778","0","","","","","","","","","" +"1","baseline","201","2026-09-29T01:42:21.6492266Z","201.002554","0.998118800000015","23.046875","0.195680614371753","","","","","","","","","" +"1","baseline","202","2026-09-29T01:42:22.6472620Z","202.0004469","0.997892899999982","14.6484375","0","","","","","","","","","" +"1","baseline","203","2026-09-29T01:42:23.6580020Z","203.0111328","1.0106859","10.3846153846154","0","","","","","","","","","" +"1","baseline","204","2026-09-29T01:42:24.6583169Z","204.0116545","1.00052170000001","11.5234375","0","","","","","","","","","" +"1","baseline","205","2026-09-29T01:42:25.6533091Z","205.0066678","0.995013300000011","9.5703125","0","","","","","","","","","" +"1","baseline","206","2026-09-29T01:42:26.6515385Z","206.0047958","0.99812799999998","14.0594059405941","0","","","","","","","","","" +"1","baseline","207","2026-09-29T01:42:27.6501672Z","207.0033457","0.9985499","15.6555772994129","0.195596133953846","","","","","","","","","" +"1","baseline","208","2026-09-29T01:42:28.6530191Z","208.0062888","1.00294310000001","10.3846153846154","0","","","","","","","","","" +"1","baseline","209","2026-09-29T01:42:29.6531913Z","209.0065441","1.00025529999999","5.859375","0","","","","","","","","","" +"1","baseline","210","2026-09-29T01:42:30.6507633Z","210.0039848","0.997440699999999","9.72222222222222","0","","","","","","","","","" +"1","baseline","211","2026-09-29T01:42:31.6469173Z","211.0001685","0.996183700000017","14.84375","0","","","","","","","","","" +"1","baseline","212","2026-09-29T01:42:32.6589750Z","212.0123732","1.01220469999998","5.96153846153846","0","","","","","","","","","" +"1","baseline","213","2026-09-29T01:42:33.6600271Z","213.0133051","1.00093190000001","7.6171875","0","","","","","","","","","" +"1","baseline","214","2026-09-29T01:42:34.6583788Z","214.0117129","0.998407799999995","5.6640625","0","","","","","","","","","" +"1","baseline","215","2026-09-29T01:42:35.6557029Z","215.0089072","0.99719429999999","14.0625","0","","","","","","","","","" +"1","baseline","216","2026-09-29T01:42:36.6567421Z","216.0099344","1.00102720000001","7.6171875","0","","","","","","","","","" +"1","baseline","217","2026-09-29T01:42:37.6584732Z","217.011694","1.00175959999999","6.640625","0","","","","","","","","","" +"1","baseline","218","2026-09-29T01:42:38.6621860Z","218.0155454","1.0038514","5.46875","0","","","","","","","","","" +"1","baseline","219","2026-09-29T01:42:39.6481956Z","219.001357","0.985811600000005","9.12698412698413","0","","","","","","","","","" +"1","baseline","220","2026-09-29T01:42:40.6497707Z","220.0029416","1.0015846","6.8359375","0","","","","","","","","","" +"1","baseline","221","2026-09-29T01:42:41.6513711Z","221.0045328","1.00159120000001","16.2109375","0","","","","","","","","","" +"1","baseline","222","2026-09-29T01:42:42.6564056Z","222.0096115","1.00507869999998","10.1923076923077","0.194325578683543","","","","","","","","","" +"1","baseline","223","2026-09-29T01:42:43.6541406Z","223.0074786","0.997867100000008","6.8359375","0","","","","","","","","","" +"1","baseline","224","2026-09-29T01:42:44.6525597Z","224.0058399","0.998361299999999","5.859375","0","","","","","","","","","" +"1","baseline","225","2026-09-29T01:42:45.6531308Z","225.006318","1.00047810000001","5.2734375","0","","","","","","","","","" +"1","baseline","226","2026-09-29T01:42:46.6529922Z","226.0061434","0.999825399999992","24.21875","0","","","","","","","","","" +"1","baseline","227","2026-09-29T01:42:47.6537184Z","227.0070961","1.0009527","5.859375","0","","","","","","","","","" +"1","baseline","228","2026-09-29T01:42:48.6544630Z","228.0076628","1.00056670000001","6.25","0","","","","","","","","","" +"1","baseline","229","2026-09-29T01:42:49.6521214Z","229.0053733","0.997710500000011","6.640625","0","","","","","","","","","" +"1","baseline","230","2026-09-29T01:42:50.6529739Z","230.0061358","1.00076249999998","6.25","0","","","","","","","","","" +"1","baseline","231","2026-09-29T01:42:51.6529573Z","231.0061825","1.00004670000001","11.3502935420744","0","","","","","","","","","" +"1","baseline","232","2026-09-29T01:42:52.6609011Z","232.0141736","1.0079911","6.4327485380117","0","","","","","","","","","" +"1","baseline","233","2026-09-29T01:42:53.6484080Z","233.0017584","0.987584800000008","5.35714285714286","0","","","","","","","","","" +"1","baseline","234","2026-09-29T01:42:54.6488216Z","234.0021705","1.00041209999998","7.6171875","0","","","","","","","","","" +"1","baseline","235","2026-09-29T01:42:55.6473031Z","235.0006635","0.998493000000025","9.1796875","0","","","","","","","","","" +"1","baseline","236","2026-09-29T01:42:56.6585357Z","236.0118712","1.0112077","7.69230769230769","0","","","","","","","","","" +"1","baseline","237","2026-09-29T01:42:57.6577439Z","237.0109665","0.999095299999993","5.46875","0","","","","","","","","","" +"1","baseline","238","2026-09-29T01:42:58.6560933Z","238.0093305","0.998363999999981","7.8125","0","","","","","","","","","" +"1","baseline","239","2026-09-29T01:42:59.6574436Z","239.010794","1.0014635","5.078125","0","","","","","","","","","" +"1","baseline","240","2026-09-29T01:43:00.6580856Z","240.0114572","1.00066320000002","6.25","0","","","","","","","","","" +"1","baseline","241","2026-09-29T01:43:01.6599943Z","241.0132993","1.0018421","5.859375","0","","","","","","","","","" +"1","baseline","242","2026-09-29T01:43:02.6614693Z","242.014777","1.00147769999998","5.859375","0","","","","","","","","","" +"1","baseline","243","2026-09-29T01:43:03.6477864Z","243.0009467","0.986169700000005","5.95238095238095","0","","","","","","","","","" +"1","baseline","244","2026-09-29T01:43:04.6612859Z","244.0145052","1.01355850000002","6.53846153846154","0","","","","","","","","","" +"1","baseline","245","2026-09-29T01:43:05.6582129Z","245.0115421","0.997036899999983","5.859375","0","","","","","","","","","" +"1","baseline","246","2026-09-29T01:43:06.6572397Z","246.0104262","0.998884099999998","9.1796875","0","","","","","","","","","" +"1","baseline","247","2026-09-29T01:43:07.6579559Z","247.011323","1.00089679999999","8.203125","0","","","","","","","","","" +"1","baseline","248","2026-09-29T01:43:08.6470803Z","248.0003905","0.989067500000004","4.16666666666667","0","","","","","","","","","" +"1","baseline","249","2026-09-29T01:43:09.6506472Z","249.0038612","1.00347070000001","4.4921875","0","","","","","","","","","" +"1","baseline","250","2026-09-29T01:43:10.6493515Z","250.0025615","0.998700299999996","15.0390625","0","","","","","","","","","" +"1","baseline","251","2026-09-29T01:43:11.6501820Z","251.0040047","1.00144320000001","10.546875","0","","","","","","","","","" +"1","baseline","252","2026-09-29T01:43:12.6509056Z","252.0042511","1.00024639999998","8.7890625","0","","","","","","","","","" +"1","baseline","253","2026-09-29T01:43:13.6536238Z","253.0068683","1.0026172","13.0769230769231","0","","","","","","","","","" +"1","baseline","254","2026-09-29T01:43:14.6507991Z","254.0034878","0.996619500000008","7.34126984126984","0","","","","","","","","","" +"1","baseline","255","2026-09-29T01:43:15.6498880Z","255.0032024","0.999714600000004","9.1796875","0","","","","","","","","","" +"1","baseline","256","2026-09-29T01:43:16.6491345Z","256.002461","0.99925859999999","14.84375","0","","","","","","","","","" +"1","baseline","257","2026-09-29T01:43:17.6526912Z","257.0059269","1.00346589999998","9.80769230769231","0","","","","","","","","","" +"1","baseline","258","2026-09-29T01:43:18.6511192Z","258.0044342","0.998507300000028","11.5079365079365","0","","","","","","","","","" +"1","baseline","259","2026-09-29T01:43:19.6551020Z","259.0082928","1.0038586","8.67052023121387","0.194561763977516","","","","","","","","","" +"1","baseline","260","2026-09-29T01:43:20.6588185Z","260.0120773","1.00378449999999","5.84795321637427","0","","","","","","","","","" +"1","baseline","261","2026-09-29T01:43:21.6616790Z","261.0149877","1.00291040000002","5.2734375","0","","","","","","","","","" +"1","baseline","262","2026-09-29T01:43:22.6578869Z","262.0112213","0.996233599999982","7.6171875","0","","","","","","","","","" +"1","baseline","263","2026-09-29T01:43:23.6503069Z","263.0037938","0.992572499999994","19.6428571428571","0","","","","","","","","","" +"1","baseline","264","2026-09-29T01:43:24.6522640Z","264.0054513","1.00165750000002","10.9615384615385","0","","","","","","","","","" +"1","baseline","265","2026-09-29T01:43:25.6611064Z","265.0144419","1.0089906","6.4453125","0","","","","","","","","","" +"1","baseline","266","2026-09-29T01:43:26.6560102Z","266.0094194","0.994977500000005","8.203125","0","","","","","","","","","" +"1","baseline","267","2026-09-29T01:43:27.6580949Z","267.0111977","1.00177829999996","9.375","0","","","","","","","","","" +"1","baseline","268","2026-09-29T01:43:28.6562579Z","268.0094891","0.998291400000028","21.6796875","0","","","","","","","","","" +"1","baseline","269","2026-09-29T01:43:29.6496472Z","269.0029178","0.993428699999981","19.8412698412698","0","","","","","","","","","" +"1","baseline","270","2026-09-29T01:43:30.6609945Z","270.0145986","1.01168080000002","27.1153846153846","0","","","","","","","","","" +"1","baseline","271","2026-09-29T01:43:31.6576531Z","271.0110863","0.996487699999989","14.84375","0","","","","","","","","","" +"1","baseline","272","2026-09-29T01:43:32.6560872Z","272.0094594","0.998373099999981","8.984375","0","","","","","","","","","" +"1","baseline","273","2026-09-29T01:43:33.6530322Z","273.0062971","0.996837700000015","6.4453125","0","","","","","","","","","" +"1","baseline","274","2026-09-29T01:43:34.6519535Z","274.0047921","0.998494999999991","8.64440078585462","0.195606888366994","","","","","","","","","" +"1","baseline","275","2026-09-29T01:43:35.6524244Z","275.0057256","1.00093350000003","13.9805825242718","0","","","","","","","","","" +"1","baseline","276","2026-09-29T01:43:36.6564063Z","276.0095961","1.0038705","7.8125","0","","","","","","","","","" +"1","baseline","277","2026-09-29T01:43:37.6552578Z","277.0081095","0.998513399999979","7.6171875","0","","","","","","","","","" +"1","baseline","278","2026-09-29T01:43:38.6550481Z","278.0083103","1.00020080000002","8.59375","0","","","","","","","","","" +"1","baseline","279","2026-09-29T01:43:39.6570527Z","279.0102521","1.0019418","7.03125","0","","","","","","","","","" +"1","baseline","280","2026-09-29T01:43:40.6540958Z","280.0073568","0.997104699999966","4.70588235294118","0","","","","","","","","","" +"1","baseline","281","2026-09-29T01:43:41.6523077Z","281.0056338","0.99827700000003","5.05836575875486","0","","","","","","","","","" +"1","baseline","282","2026-09-29T01:43:42.6604118Z","282.0138322","1.00819839999997","12.3046875","0","","","","","","","","","" +"1","baseline","283","2026-09-29T01:43:43.6574650Z","283.0107692","0.996937000000003","5.078125","0","","","","","","","","","" +"1","baseline","284","2026-09-29T01:43:44.6613363Z","284.0145575","1.0037883","8.7890625","0","","","","","","","","","" +"1","baseline","285","2026-09-29T01:43:45.6470725Z","285.0004646","0.98590710000002","7.14285714285714","0","","","","","","","","","" +"1","baseline","286","2026-09-29T01:43:46.6603966Z","286.0136732","1.01320859999998","11.1538461538462","0","","","","","","","","","" +"1","baseline","287","2026-09-29T01:43:47.6600254Z","287.0132986","0.999625400000014","5.2734375","0","","","","","","","","","" +"1","baseline","288","2026-09-29T01:43:48.6586947Z","288.0119366","0.998638000000028","4.8828125","0","","","","","","","","","" +"1","baseline","289","2026-09-29T01:43:49.6594029Z","289.0127197","1.00078309999998","6.4453125","0","","","","","","","","","" +"1","baseline","290","2026-09-29T01:43:50.6551006Z","290.0084304","0.995710700000018","5.2734375","0","","","","","","","","","" +"1","baseline","291","2026-09-29T01:43:51.6569702Z","291.0097256","1.00129519999996","7.2265625","0","","","","","","","","","" +"1","baseline","292","2026-09-29T01:43:52.6545214Z","292.0077745","0.998048900000015","6.4453125","0","","","","","","","","","" +"1","baseline","293","2026-09-29T01:43:53.6495536Z","293.0028377","0.995063200000004","14.484126984127","0","","","","","","","","","" +"1","baseline","294","2026-09-29T01:43:54.6512564Z","294.004483","1.00164530000001","13.4765625","0","","","","","","","","","" +"1","baseline","295","2026-09-29T01:43:55.6468251Z","295.0001408","0.995657800000004","8.3984375","0.1961642845564","","","","","","","","","" +"1","baseline","296","2026-09-29T01:43:56.6516319Z","296.0048885","1.0047477","12.3552123552124","0","","","","","","","","","" +"1","baseline","297","2026-09-29T01:43:57.6490216Z","297.0021833","0.99729480000002","9.28853754940712","0.195842292569856","","","","","","","","","" +"1","baseline","298","2026-09-29T01:43:58.6468533Z","298.0000972","0.997913899999958","7.8125","0","","","","","","","","","" +"1","baseline","299","2026-09-29T01:43:59.6503813Z","299.0034722","1.00337500000001","5.859375","0","","","","","","","","","" +"1","baseline","300","2026-09-29T01:44:00.6585121Z","300.0117647","1.00829250000004","13.6538461538462","0","","","","","","","","","" diff --git a/docs/benchmarks/2026-09-29-v0.2.0/results/run-1-block-end.json b/docs/benchmarks/2026-09-29-v0.2.0/results/run-1-block-end.json new file mode 100644 index 0000000..0b844dd --- /dev/null +++ b/docs/benchmarks/2026-09-29-v0.2.0/results/run-1-block-end.json @@ -0,0 +1,12 @@ +{ + "state": "block", + "layers": { + "shutdown": true, + "local_shutdown": true, + "update": false, + "remote": true, + "sleep": true + }, + "uptime_seconds": 333, + "blocked_count": 0 +} diff --git a/docs/benchmarks/2026-09-29-v0.2.0/results/run-1-block-start.json b/docs/benchmarks/2026-09-29-v0.2.0/results/run-1-block-start.json new file mode 100644 index 0000000..5994d76 --- /dev/null +++ b/docs/benchmarks/2026-09-29-v0.2.0/results/run-1-block-start.json @@ -0,0 +1,12 @@ +{ + "state": "block", + "layers": { + "shutdown": true, + "local_shutdown": true, + "update": false, + "remote": true, + "sleep": true + }, + "uptime_seconds": 33, + "blocked_count": 0 +} diff --git a/docs/benchmarks/2026-09-29-v0.2.0/results/run-1-block.csv b/docs/benchmarks/2026-09-29-v0.2.0/results/run-1-block.csv new file mode 100644 index 0000000..6636f91 --- /dev/null +++ b/docs/benchmarks/2026-09-29-v0.2.0/results/run-1-block.csv @@ -0,0 +1,301 @@ +"run","scenario","sample","utc","elapsed_s","interval_s","system_cpu_pct","collector_cpu_pct","pid","process_cpu_s","cpu_pct","private_mib","working_set_mib","threads","handles","io_read_bytes_s","io_write_bytes_s" +"1","block","1","2026-09-29T01:50:08.0115577Z","1.0048423","1.0048388","8.65384615384615","0","26324","0","0","3.23828125","13.92578125","11","194","0","0" +"1","block","2","2026-09-29T01:50:09.0128938Z","2.0061387","1.0012964","11.5234375","0","26324","0","0","3.296875","14.1171875","11","194","0","0" +"1","block","3","2026-09-29T01:50:10.0168402Z","3.010124","1.0039853","7.421875","0","26324","0","0","3.234375","14.0390625","11","194","0","0" +"1","block","4","2026-09-29T01:50:11.0180048Z","4.0112522","1.0011282","5.6640625","0","26324","0","0","3.234375","14.0390625","11","194","0","0" +"1","block","5","2026-09-29T01:50:12.0195411Z","5.0128834","1.0016312","7.6171875","0","26324","0","0","3.234375","14.0390625","11","194","0","0" +"1","block","6","2026-09-29T01:50:13.0211117Z","6.0143865","1.0015031","19.921875","0","26324","0","0","3.25390625","14.04296875","11","194","0","0" +"1","block","7","2026-09-29T01:50:14.0072613Z","7.0004857","0.9860992","18.8492063492063","0","26324","0","0","3.25390625","14.046875","11","194","0","0" +"1","block","8","2026-09-29T01:50:15.0087170Z","8.0019415","1.0014558","10.3515625","0","26324","0","0","3.25390625","14.046875","11","194","0","0" +"1","block","9","2026-09-29T01:50:16.0084889Z","9.0018508","0.999909300000001","7.6171875","0.195330216450632","26324","0","0","3.25390625","14.046875","11","194","0","0" +"1","block","10","2026-09-29T01:50:17.0095041Z","10.0026967","1.0008459","5.2734375","0.195147424793367","26324","0","0","3.25390625","14.05078125","11","194","0","0" +"1","block","11","2026-09-29T01:50:18.0103799Z","11.0030783","1.0003816","13.0859375","0.195237997180276","26324","0","0","3.25390625","14.05078125","11","194","0","0" +"1","block","12","2026-09-29T01:50:19.0185519Z","12.011297","1.0082187","9.80769230769231","0.193720370391861","26324","0","0","3.25390625","14.05078125","11","194","0","0" +"1","block","13","2026-09-29T01:50:20.0353790Z","13.0285439","1.0172469","7.30769230769231","0","26324","0","0","3.25390625","14.0546875","11","194","0","0" +"1","block","14","2026-09-29T01:50:21.0171509Z","14.0104799","0.981936000000001","5.35714285714286","0","26324","0","0","3.25390625","14.0546875","11","194","0","0" +"1","block","15","2026-09-29T01:50:22.0125786Z","15.005971","0.995491099999999","9.5703125","0","26324","0","0","3.25390625","14.0546875","11","194","0","0" +"1","block","16","2026-09-29T01:50:23.0221461Z","16.0155945","1.0096235","14.453125","0","26324","0","0","3.25390625","14.0546875","11","194","0","0" +"1","block","17","2026-09-29T01:50:24.0089247Z","17.0021408","0.986546300000001","6.54761904761905","0","26324","0","0","3.25390625","14.0546875","11","194","0","0" +"1","block","18","2026-09-29T01:50:25.0062578Z","17.9995949","0.997454099999999","5.46875","0.19581101526376","26324","0","0","3.25390625","14.0546875","11","194","0","0" +"1","block","19","2026-09-29T01:50:26.0073724Z","19.0005878","1.0009929","7.2265625","0","26324","0","0","3.25390625","14.0546875","11","194","0","0" +"1","block","20","2026-09-29T01:50:27.0156040Z","20.0088338","1.008246","7.88461538461539","0","26324","0","0","3.25390625","14.0546875","11","194","0","0" +"1","block","21","2026-09-29T01:50:28.0163458Z","21.0096595","1.0008257","5.6640625","0","26324","0","0","3.25390625","14.0546875","11","194","0","0" +"1","block","22","2026-09-29T01:50:29.0180428Z","22.0112706","1.0016111","7.6171875","0","26324","0","0","3.25390625","14.0546875","11","194","0","0" +"1","block","23","2026-09-29T01:50:30.0146654Z","23.0079881","0.996717499999999","8.59375","0","26324","0","0","3.25390625","14.0546875","11","194","0","0" +"1","block","24","2026-09-29T01:50:31.0155597Z","24.0088071","1.000819","5.6640625","0","26324","0","0","3.25390625","14.0546875","11","194","0","0" +"1","block","25","2026-09-29T01:50:32.0135042Z","25.0067054","0.997898299999999","8.203125","0","26324","0","0","3.25390625","14.0546875","11","194","0","0" +"1","block","26","2026-09-29T01:50:33.0105657Z","26.003742","0.997036600000001","10.3174603174603","0","26324","0","0","3.25390625","14.05859375","11","194","0","0" +"1","block","27","2026-09-29T01:50:34.0096100Z","27.0028373","0.9990953","31.25","0.195489359223289","26324","0","0","3.22265625","14.02734375","10","194","0","0" +"1","block","28","2026-09-29T01:50:35.0134666Z","28.0067079","1.0038706","13.8461538461538","0.194559438238355","26324","0","0","3.22265625","14.02734375","10","194","0","0" +"1","block","29","2026-09-29T01:50:36.0107432Z","29.0039525","0.997244599999998","11.5079365079365","0","26324","0.015625","0.195852151016912","3.2265625","14.03125","10","194","0","0" +"1","block","30","2026-09-29T01:50:37.0127555Z","30.0059897","1.0020372","45.7692307692308","0.194915418309819","26324","0","0","3.2265625","14.03125","10","194","0","0" +"1","block","31","2026-09-29T01:50:38.0227202Z","31.0159925","1.0100028","16.2109375","0","26324","0","0","3.2265625","14.03125","10","194","0","0" +"1","block","32","2026-09-29T01:50:39.0174015Z","32.0107081","0.994715599999996","15.4296875","0","26324","0","0","3.2265625","14.03515625","10","194","0","0" +"1","block","33","2026-09-29T01:50:40.0142526Z","33.0075001","0.996792000000006","10.9375","0","26324","0","0","3.2265625","14.0390625","10","194","0","0" +"1","block","34","2026-09-29T01:50:41.0168198Z","34.0101228","1.0026227","5.078125","0","26324","0","0","3.2265625","14.0390625","10","194","0","0" +"1","block","35","2026-09-29T01:50:42.0165956Z","35.0099419","0.999819100000003","11.71875","0","26324","0","0","3.2265625","14.04296875","10","194","0","0" +"1","block","36","2026-09-29T01:50:43.0168116Z","36.0100449","1.000103","4.6875","0","26324","0","0","3.2265625","14.04296875","10","194","0","0" +"1","block","37","2026-09-29T01:50:44.0169265Z","37.0102033","1.0001584","5.2734375","0","26324","0","0","3.2265625","14.04296875","10","194","0","0" +"1","block","38","2026-09-29T01:50:45.0156251Z","38.0089782","0.998774900000001","6.0546875","0","26324","0","0","3.2265625","14.04296875","10","194","0","0" +"1","block","39","2026-09-29T01:50:46.0128627Z","39.0060343","0.997056099999995","6.640625","0","26324","0","0","3.265625","14.05078125","10","194","0","0" +"1","block","40","2026-09-29T01:50:47.0111236Z","40.0043592","0.9983249","20.5479452054795","0","26324","0","0","3.265625","14.05078125","10","194","0","0" +"1","block","41","2026-09-29T01:50:48.0221417Z","41.015604","1.0112448","7.40740740740741","0.193140671774035","26324","0","0","3.265625","14.05078125","10","194","0","0" +"1","block","42","2026-09-29T01:50:49.0095766Z","42.0028056","0.987201599999999","8.92857142857143","0","26324","0","0","3.265625","14.05078125","10","194","0","0" +"1","block","43","2026-09-29T01:50:50.0139376Z","43.0071618","1.0043562","10.7692307692308","0.1944653699554","26324","0","0","3.265625","14.05078125","10","194","0","0" +"1","block","44","2026-09-29T01:50:51.0170436Z","44.0103588","1.003197","4.4921875","0","26324","0","0","3.265625","14.05078125","10","194","0","0" +"1","block","45","2026-09-29T01:50:52.0146216Z","45.0078587","0.997499900000001","9.1796875","0","26324","0","0","3.265625","14.05078125","10","194","0","0" +"1","block","46","2026-09-29T01:50:53.0146115Z","46.0077827","0.999924","4.4921875","0","26324","0","0","3.265625","14.05078125","10","194","0","0" +"1","block","47","2026-09-29T01:50:54.0118561Z","47.0050852","0.997302499999996","5.46875","0","26324","0","0","3.265625","14.05078125","10","194","0","0" +"1","block","48","2026-09-29T01:50:55.0183852Z","48.0115629","1.0064777","5.859375","0","26324","0","0","3.265625","14.05078125","10","194","0","0" +"1","block","49","2026-09-29T01:50:56.0110635Z","49.0043618","0.992798899999997","5.859375","0","26324","0","0","3.265625","14.05078125","10","194","0","0" +"1","block","50","2026-09-29T01:50:57.0114885Z","50.0047303","1.0003685","8.203125","0","26324","0","0","3.265625","14.05078125","10","194","0","0" +"1","block","51","2026-09-29T01:50:58.0143990Z","51.0075418","1.0028115","15.234375","0","26324","0","0","3.265625","14.05078125","10","194","0","0" +"1","block","52","2026-09-29T01:50:59.0185088Z","52.0122784","1.0047366","6.25","0","26324","0","0","3.265625","14.05078125","10","194","0","0" +"1","block","53","2026-09-29T01:51:00.0192245Z","53.0124017","1.0001233","6.25","0","26324","0","0","3.265625","14.05078125","10","194","0","0" +"1","block","54","2026-09-29T01:51:01.0196788Z","54.0130302","1.0006285","7.2265625","0","26324","0","0","3.265625","14.05078125","10","194","0","0" +"1","block","55","2026-09-29T01:51:02.0191664Z","55.0123647","0.999334500000003","12.890625","0","26324","0","0","3.265625","14.05078125","10","194","0","0" +"1","block","56","2026-09-29T01:51:03.0221898Z","56.0149847","1.00262","5.2734375","0","26324","0","0","3.265625","14.05078125","10","194","0","0" +"1","block","57","2026-09-29T01:51:04.0186703Z","57.0119975","0.9970128","6.25","0","26324","0","0","3.234375","14.0390625","10","194","0","0" +"1","block","58","2026-09-29T01:51:05.0215923Z","58.01474","1.0027425","5.46875","0","26324","0","0","3.234375","14.0390625","10","194","0","0" +"1","block","59","2026-09-29T01:51:06.0086680Z","59.0019679","0.987227900000001","10.1190476190476","0","26324","0","0","3.234375","14.0390625","10","194","0","0" +"1","block","60","2026-09-29T01:51:07.0140706Z","60.0073736","1.0054057","9.03846153846154","0.194262375874734","26324","0","0","3.234375","14.0390625","10","194","0","0" +"1","block","61","2026-09-29T01:51:08.0130791Z","61.0063581","0.998984500000006","5.078125","0","26324","0","0","3.234375","14.0390625","10","194","0","0" +"1","block","62","2026-09-29T01:51:09.0098838Z","62.0031654","0.9968073","8.53174603174603","0","26324","0","0","3.234375","14.0390625","10","194","0","0" +"1","block","63","2026-09-29T01:51:10.0090304Z","63.002317","0.999151599999998","9.1796875","0.195478343826903","26324","0","0","3.234375","14.0390625","10","194","0","0" +"1","block","64","2026-09-29T01:51:11.0082938Z","64.0014337","0.999116699999995","7.2265625","0.195485172052475","26324","0","0","3.234375","14.0390625","10","194","0","0" +"1","block","65","2026-09-29T01:51:12.0213139Z","65.0145638","1.0131301","8.46153846153846","0","26324","0","0","3.234375","14.0390625","10","194","0","0" +"1","block","66","2026-09-29T01:51:13.0102507Z","66.0033909","0.988827100000009","7.3558648111332","0","26324","0","0","3.234375","14.04296875","10","194","0","0" +"1","block","67","2026-09-29T01:51:14.0160942Z","67.0094936","1.0061027","10.1727447216891","0.194127796297535","26324","0","0","3.234375","14.04296875","10","194","0","0" +"1","block","68","2026-09-29T01:51:15.0161768Z","68.0093847","0.999891099999999","6.25","0","26324","0","0","3.234375","14.046875","10","194","0","0" +"1","block","69","2026-09-29T01:51:16.0185143Z","69.0117874","1.0024027","4.8828125","0","26324","0","0","3.234375","14.046875","10","194","0","0" +"1","block","70","2026-09-29T01:51:17.0211795Z","70.0145073","1.00271989999999","7.421875","0","26324","0","0","3.234375","14.046875","10","194","0","0" +"1","block","71","2026-09-29T01:51:18.0143640Z","71.0076593","0.993152000000009","5.6640625","0","26324","0","0","3.234375","14.046875","10","194","0","0" +"1","block","72","2026-09-29T01:51:19.0143994Z","72.0076692","1.00000989999999","4.4921875","0","26324","0","0","3.234375","14.046875","10","194","0","0" +"1","block","73","2026-09-29T01:51:20.0193584Z","73.0124954","1.0048262","6.8359375","0","26324","0","0","3.234375","14.046875","10","194","0","0" +"1","block","74","2026-09-29T01:51:21.0067364Z","73.9999285","0.987433100000004","5.75396825396825","0","26324","0","0","3.234375","14.05078125","10","194","0","0" +"1","block","75","2026-09-29T01:51:22.0220680Z","75.0154453","1.0155168","12.3076923076923","0.192328182064541","26324","0","0","3.234375","14.05078125","10","194","0","0" +"1","block","76","2026-09-29T01:51:23.0067113Z","76.0000139","0.984568600000003","8.33333333333333","0","26324","0","0","3.234375","14.05078125","10","194","0","0" +"1","block","77","2026-09-29T01:51:24.0070799Z","77.0004519","1.000438","8.984375","0","26324","0","0","3.234375","14.0546875","10","194","0","0" +"1","block","78","2026-09-29T01:51:25.0134468Z","78.0067575","1.00630559999999","21.5384615384615","0","26324","0","0","3.234375","14.0546875","10","194","0","0" +"1","block","79","2026-09-29T01:51:26.0165841Z","79.0098809","1.00312340000001","7.421875","0","26324","0","0","3.234375","14.0546875","10","194","0","0" +"1","block","80","2026-09-29T01:51:27.0147051Z","80.0079005","0.998019599999992","5.46875","0","26324","0","0","3.234375","14.0546875","10","194","0","0" +"1","block","81","2026-09-29T01:51:28.0141775Z","81.0074435","0.999543000000003","9.375","0","26324","0","0","3.234375","14.0546875","10","194","0","0" +"1","block","82","2026-09-29T01:51:29.0163380Z","82.0096629","1.0022194","7.421875","0","26324","0","0","3.234375","14.0546875","10","194","0","0" +"1","block","83","2026-09-29T01:51:30.0138260Z","83.0071088","0.997445900000002","7.6171875","0","26324","0","0","3.234375","14.0546875","10","194","0","0" +"1","block","84","2026-09-29T01:51:31.0158400Z","84.0091021","1.0019933","4.8828125","0","26324","0","0","3.234375","14.0546875","10","194","0","0" +"1","block","85","2026-09-29T01:51:32.0147147Z","85.0079933","0.998891200000003","6.0546875","0","26324","0","0","3.234375","14.0546875","10","194","0","0" +"1","block","86","2026-09-29T01:51:33.0151154Z","86.0082576","1.0002643","8.984375","0","26324","0","0","3.234375","14.0546875","10","194","0","0" +"1","block","87","2026-09-29T01:51:34.0216777Z","87.0150617","1.0068041","7.421875","0","26324","0","0","3.23046875","14.0390625","10","194","0","0" +"1","block","88","2026-09-29T01:51:35.0077268Z","88.0008143","0.985752600000012","6.54761904761905","0","26324","0","0","3.23046875","14.0390625","10","194","0","0" +"1","block","89","2026-09-29T01:51:36.0218075Z","89.01507","1.01425569999999","4.80769230769231","0","26324","0","0","3.23046875","14.0390625","10","194","0","0" +"1","block","90","2026-09-29T01:51:37.0161660Z","90.0093656","0.994295600000001","10.3515625","0","26324","0","0","3.23046875","14.0390625","10","194","0","0" +"1","block","91","2026-09-29T01:51:38.0171661Z","91.0104712","1.0011056","5.859375","0","26324","0","0","3.23046875","14.0390625","10","194","0","0" +"1","block","92","2026-09-29T01:51:39.0197754Z","92.0131239","1.0026527","4.4921875","0","26324","0","0","3.23046875","14.0390625","10","194","0","0" +"1","block","93","2026-09-29T01:51:40.0064858Z","92.99966","0.986536099999995","6.15079365079365","0","26324","0","0","3.23046875","14.0390625","10","194","0","0" +"1","block","94","2026-09-29T01:51:41.0073765Z","94.0005969","1.0009369","6.0546875","0","26324","0","0","3.23046875","14.0390625","10","194","0","0" +"1","block","95","2026-09-29T01:51:42.0105402Z","95.0033664","1.0027695","10.3515625","0.194773075966112","26324","0","0","3.23046875","14.0390625","10","194","0","0" +"1","block","96","2026-09-29T01:51:43.0075468Z","96.0007713","0.997404900000006","12.6953125","0.195820674231697","26324","0","0","3.23046875","14.0390625","10","194","0","0" +"1","block","97","2026-09-29T01:51:44.0078974Z","97.0010803","1.000309","4.6875","0","26324","0","0","3.234375","14.04296875","10","194","0","0" +"1","block","98","2026-09-29T01:51:45.0186490Z","98.0119705","1.01089019999999","6.53846153846154","0.193208421646586","26324","0","0","3.234375","14.04296875","10","194","0","0" +"1","block","99","2026-09-29T01:51:46.0212548Z","99.0145332","1.00256270000001","8.0078125","0","26324","0","0","3.234375","14.04296875","10","194","0","0" +"1","block","100","2026-09-29T01:51:47.0207300Z","100.013898","0.999364799999995","6.0546875","0","26324","0","0","3.234375","14.04296875","10","194","0","0" +"1","block","101","2026-09-29T01:51:48.0183321Z","101.0115815","0.997683499999994","5.6640625","0","26324","0","0","3.203125","14.02734375","9","192","0","0" +"1","block","102","2026-09-29T01:51:49.0078243Z","102.0011565","0.989575000000002","5.55555555555556","0","26324","0","0","3.203125","14.03125","9","192","0","0" +"1","block","103","2026-09-29T01:51:50.0201094Z","103.0132586","1.01210210000001","12.1153846153846","0.192977072174832","26324","0","0","3.203125","14.03515625","9","192","0","0" +"1","block","104","2026-09-29T01:51:51.0223841Z","104.0157927","1.00253409999999","8.0078125","0","26324","0","0","3.203125","14.03515625","9","192","0","0" +"1","block","105","2026-09-29T01:51:52.0165027Z","105.0097678","0.9939751","20.5078125","0","26324","0","0","3.203125","14.03515625","9","192","0","0" +"1","block","106","2026-09-29T01:51:53.0123347Z","106.0056108","0.995843000000008","43.5546875","0","26324","0","0","3.203125","14.0390625","9","192","0","0" +"1","block","107","2026-09-29T01:51:54.0184688Z","107.0112383","1.0056275","13.4765625","0","26324","0","0","3.203125","14.0390625","9","192","0","0" +"1","block","108","2026-09-29T01:51:55.0143705Z","108.0076764","0.996438099999992","5.67514677103718","0","26324","0","0","3.203125","14.0390625","9","192","0","0" +"1","block","109","2026-09-29T01:51:56.0096980Z","109.0030353","0.995358899999999","11.2871287128713","0","26324","0","0","3.203125","14.0390625","9","192","0","0" +"1","block","110","2026-09-29T01:51:57.0102492Z","110.0034579","1.00042260000001","15.0390625","0.195229995803772","26324","0.015625","0.195229995803772","3.203125","14.0390625","9","192","0","0" +"1","block","111","2026-09-29T01:51:58.0072891Z","111.0004526","0.996994699999988","9.1796875","0.195901242002593","26324","0","0","3.203125","14.0390625","9","192","0","0" +"1","block","112","2026-09-29T01:51:59.0191743Z","112.0123872","1.0119346","4.42307692307692","0","26324","0","0","3.203125","14.0390625","9","192","0","0" +"1","block","113","2026-09-29T01:52:00.0161203Z","113.0094219","0.9970347","5.859375","0","26324","0","0","3.203125","14.0390625","9","192","0","0" +"1","block","114","2026-09-29T01:52:01.0168881Z","114.010086","1.00066410000001","6.25","0","26324","0","0","3.203125","14.0390625","9","192","0","0" +"1","block","115","2026-09-29T01:52:02.0221588Z","115.0154712","1.00538519999999","9.765625","0","26324","0","0","3.203125","14.0390625","9","192","0","0" +"1","block","116","2026-09-29T01:52:03.0165540Z","116.0011979","0.985726700000001","8.92857142857143","0","26324","0","0","3.203125","14.0390625","9","192","0","0" +"1","block","117","2026-09-29T01:52:04.0077755Z","117.0011795","0.999981599999998","8.203125","0","26324","0","0","3.2109375","14.03125","10","192","0","0" +"1","block","118","2026-09-29T01:52:05.0210706Z","118.0143194","1.0131399","6.53846153846154","0","26324","0","0","3.2109375","14.03125","10","192","0","0" +"1","block","119","2026-09-29T01:52:06.0191774Z","119.0123687","0.998049300000005","7.8125","0","26324","0","0","3.2109375","14.03125","10","192","0","0" +"1","block","120","2026-09-29T01:52:07.0221843Z","120.0155161","1.0031474","6.0546875","0","26324","0","0","3.21484375","14.03515625","10","192","0","0" +"1","block","121","2026-09-29T01:52:08.0222793Z","121.0154867","0.999970599999997","4.6875","0","26324","0","0","3.21484375","14.03515625","10","192","0","0" +"1","block","122","2026-09-29T01:52:09.0221649Z","122.0149198","0.999433100000005","5.6640625","0","26324","0","0","3.21484375","14.03515625","10","192","0","0" +"1","block","123","2026-09-29T01:52:10.0202980Z","123.0136296","0.998709799999986","7.2265625","0","26324","0","0","3.21484375","14.03515625","10","192","0","0" +"1","block","124","2026-09-29T01:52:11.0167435Z","124.0099738","0.99634420000001","6.0546875","0","26324","0","0","3.21484375","14.03515625","10","192","0","0" +"1","block","125","2026-09-29T01:52:12.0157942Z","125.0090265","0.999052699999993","4.4921875","0","26324","0","0","3.21484375","14.03515625","10","192","0","0" +"1","block","126","2026-09-29T01:52:13.0183080Z","126.0115815","1.002555","8.59375","0","26324","0","0","3.21484375","14.03515625","10","192","0","0" +"1","block","127","2026-09-29T01:52:14.0169999Z","127.0103269","0.998745400000004","7.8125","0","26324","0","0","3.234375","14.0390625","10","192","0","0" +"1","block","128","2026-09-29T01:52:15.0101983Z","128.0033564","0.993029500000006","6.54761904761905","0","26324","0","0","3.234375","14.0390625","10","192","0","0" +"1","block","129","2026-09-29T01:52:16.0069584Z","129.0002845","0.996928099999991","7.8125","0.195914329227957","26324","0","0","3.234375","14.04296875","10","192","0","0" +"1","block","130","2026-09-29T01:52:17.0223461Z","130.0153479","1.0150634","5.19230769230769","0","26324","0","0","3.234375","14.04296875","10","192","0","0" +"1","block","131","2026-09-29T01:52:18.0085720Z","131.0019575","0.986609600000008","6.15079365079365","0","26324","0","0","3.234375","14.04296875","10","192","0","0" +"1","block","132","2026-09-29T01:52:19.0114319Z","132.0046803","1.00272279999999","12.3076923076923","0.194782147169689","26324","0","0","3.234375","14.046875","10","192","0","0" +"1","block","133","2026-09-29T01:52:20.0183604Z","133.011531","1.0068507","6.4453125","0","26324","0","0","3.234375","14.046875","10","192","0","0" +"1","block","134","2026-09-29T01:52:21.0365172Z","134.0298469","1.0183159","5.96153846153846","0","26324","0","0","3.234375","14.046875","10","192","0","0" +"1","block","135","2026-09-29T01:52:22.0214000Z","135.0145656","0.984718700000002","8.33333333333333","0","26324","0","0","3.234375","14.046875","10","192","0","0" +"1","block","136","2026-09-29T01:52:23.0176486Z","136.0108411","0.996275499999996","12.3046875","0","26324","0","0","3.234375","14.05078125","10","192","0","0" +"1","block","137","2026-09-29T01:52:24.0184451Z","137.0117621","1.00092100000001","9.765625","0","26324","0","0","3.234375","14.05078125","10","192","0","0" +"1","block","138","2026-09-29T01:52:25.0172460Z","138.0104177","0.998655600000006","5.859375","0","26324","0","0","3.234375","14.05078125","10","192","0","0" +"1","block","139","2026-09-29T01:52:26.0200122Z","139.0131952","1.00277749999998","8.0078125","0","26324","0","0","3.234375","14.05078125","10","192","0","0" +"1","block","140","2026-09-29T01:52:27.0198557Z","140.013121","0.9999258","5.2734375","0","26324","0","0","3.234375","14.05078125","10","192","0","0" +"1","block","141","2026-09-29T01:52:28.0183155Z","141.0115966","0.998475600000006","9.765625","0","26324","0","0","3.234375","14.05078125","10","192","0","0" +"1","block","142","2026-09-29T01:52:29.0167162Z","142.009854","0.9982574","4.8828125","0","26324","0","0","3.234375","14.05078125","10","192","0","0" +"1","block","143","2026-09-29T01:52:30.0157299Z","143.0089168","0.99906279999999","8.203125","0","26324","0","0","3.234375","14.05078125","10","192","0","0" +"1","block","144","2026-09-29T01:52:31.0137331Z","144.0069069","0.99799010000001","4.6875","0","26324","0","0","3.234375","14.05078125","10","192","0","0" +"1","block","145","2026-09-29T01:52:32.0148305Z","145.0080088","1.00110190000001","6.4453125","0","26324","0","0","3.234375","14.0546875","10","192","0","0" +"1","block","146","2026-09-29T01:52:33.0186493Z","146.0121066","1.00409779999998","9.5703125","0","26324","0","0","3.234375","14.0546875","10","192","0","0" +"1","block","147","2026-09-29T01:52:34.0193729Z","147.0126547","1.0005481","8.984375","0","26324","0","0","3.2109375","14.03125","10","194","0","0" +"1","block","148","2026-09-29T01:52:35.0076173Z","148.0008195","0.988164800000021","5.15873015873016","0","26324","0","0","3.2109375","14.03125","10","194","0","0" +"1","block","149","2026-09-29T01:52:36.0159988Z","149.0092856","1.00846609999999","7.11538461538461","0","26324","0","0","3.2109375","14.03125","10","194","0","0" +"1","block","150","2026-09-29T01:52:37.0139517Z","150.0071393","0.997853700000007","9.1796875","0","26324","0","0","3.2109375","14.03125","10","194","0","0" +"1","block","151","2026-09-29T01:52:38.0178242Z","151.0111069","1.00396759999998","6.8359375","0","26324","0","0","3.21484375","14.03515625","10","194","0","0" +"1","block","152","2026-09-29T01:52:39.0177658Z","152.0111479","1.00004100000001","5.859375","0","26324","0","0","3.21484375","14.03515625","10","194","0","0" +"1","block","153","2026-09-29T01:52:40.0196589Z","153.012928","1.00178009999999","12.5","0","26324","0","0","3.21484375","14.03515625","10","194","0","0" +"1","block","154","2026-09-29T01:52:41.0203457Z","154.0134718","1.0005438","6.640625","0","26324","0","0","3.21484375","14.03515625","10","194","0","0" +"1","block","155","2026-09-29T01:52:42.0224305Z","155.0156167","1.00214489999999","7.421875","0","26324","0","0","3.21484375","14.03515625","10","194","0","0" +"1","block","156","2026-09-29T01:52:43.0065437Z","155.9997498","0.984133100000008","7.34126984126984","0","26324","0","0","3.234375","14.0390625","10","194","0","0" +"1","block","157","2026-09-29T01:52:44.0196691Z","157.0128327","1.0130829","5.96153846153846","0","26324","0","0","3.234375","14.04296875","10","194","0","0" +"1","block","158","2026-09-29T01:52:45.0188506Z","158.0121029","0.999270200000012","5.6640625","0","26324","0","0","3.234375","14.046875","10","194","0","0" +"1","block","159","2026-09-29T01:52:46.0222317Z","159.0155317","1.00342879999999","9.375","0","26324","0","0","3.234375","14.046875","10","194","0","0" +"1","block","160","2026-09-29T01:52:47.0115904Z","160.004829","0.989297300000004","7.8125","0","26324","0","0","3.234375","14.046875","10","194","0","0" +"1","block","161","2026-09-29T01:52:48.0130181Z","161.0061655","1.00133649999998","17.3828125","0.195051813251593","26324","0","0","3.234375","14.046875","10","194","0","0" +"1","block","162","2026-09-29T01:52:49.0155380Z","162.0087489","1.00258340000002","7.6171875","0","26324","0","0","3.234375","14.046875","10","194","0","0" +"1","block","163","2026-09-29T01:52:50.0126616Z","163.0058084","0.997059500000006","9.375","0","26324","0","0","3.234375","14.05078125","10","194","0","0" +"1","block","164","2026-09-29T01:52:51.0144415Z","164.0075527","1.00174429999998","5.078125","0","26324","0","0","3.234375","14.05078125","10","194","0","0" +"1","block","165","2026-09-29T01:52:52.0178378Z","165.0110288","1.0034761","4.4921875","0","26324","0","0","3.234375","14.05078125","10","194","0","0" +"1","block","166","2026-09-29T01:52:53.0149139Z","166.0080545","0.997025699999995","6.25","0","26324","0","0","3.234375","14.05078125","10","194","0","0" +"1","block","167","2026-09-29T01:52:54.0138304Z","167.0070051","0.998950600000001","11.5234375","0","26324","0","0","3.234375","14.05078125","10","194","0","0" +"1","block","168","2026-09-29T01:52:55.0158097Z","168.0089791","1.00197400000002","5.859375","0","26324","0","0","3.234375","14.05078125","10","194","0","0" +"1","block","169","2026-09-29T01:52:56.0188137Z","169.0121285","1.00314939999998","5.859375","0","26324","0","0","3.234375","14.0546875","10","194","0","0" +"1","block","170","2026-09-29T01:52:57.0065079Z","169.9998536","0.987725100000006","7.93650793650794","0","26324","0","0","3.234375","14.0546875","10","194","0","0" +"1","block","171","2026-09-29T01:52:58.0223481Z","171.0157335","1.01587989999999","6.92307692307692","0","26324","0","0","3.234375","14.0546875","10","194","0","0" +"1","block","172","2026-09-29T01:52:59.0203580Z","172.0134748","0.997741300000001","22.65625","0","26324","0","0","3.234375","14.0546875","10","194","0","0" +"1","block","173","2026-09-29T01:53:00.0194675Z","173.0126977","0.999222900000007","7.2265625","0","26324","0","0","3.234375","14.0546875","10","194","0","0" +"1","block","174","2026-09-29T01:53:01.0179189Z","174.0110677","0.998369999999994","7.6171875","0","26324","0","0","3.234375","14.0546875","10","194","0","0" +"1","block","175","2026-09-29T01:53:02.0148350Z","175.0080238","0.996956100000006","9.1796875","0","26324","0","0","3.234375","14.0546875","10","194","0","0" +"1","block","176","2026-09-29T01:53:03.0166090Z","176.0098868","1.00186300000001","5.859375","0","26324","0","0","3.234375","14.0546875","10","194","0","0" +"1","block","177","2026-09-29T01:53:04.0169301Z","177.0102027","1.00031589999998","9.1796875","0","26324","0","0","3.234375","14.04296875","10","194","0","0" +"1","block","178","2026-09-29T01:53:05.0191210Z","178.0123588","1.00215610000001","7.03125","0","26324","0","0","3.234375","14.04296875","10","194","0","0" +"1","block","179","2026-09-29T01:53:06.0212843Z","179.0145297","1.00217090000001","6.640625","0","26324","0","0","3.234375","14.04296875","10","194","0","0" +"1","block","180","2026-09-29T01:53:07.0105566Z","180.0036959","0.9891662","9.52380952380952","0","26324","0","0","3.234375","14.04296875","10","194","0","0" +"1","block","181","2026-09-29T01:53:08.0176036Z","181.0107941","1.0070982","5.19230769230769","0.193935904165055","26324","0","0","3.234375","14.04296875","10","194","0","0" +"1","block","182","2026-09-29T01:53:09.0182967Z","182.0115692","1.0007751","4.296875","0","26324","0","0","3.234375","14.04296875","10","194","0","0" +"1","block","183","2026-09-29T01:53:10.0343268Z","183.0274997","1.0159305","8.07692307692308","0","26324","0","0","3.234375","14.04296875","10","194","0","0" +"1","block","184","2026-09-29T01:53:11.0133774Z","184.0066175","0.979117800000012","8.73015873015873","0","26324","0","0","3.234375","14.04296875","10","194","0","0" +"1","block","185","2026-09-29T01:53:12.0127268Z","185.0060001","0.99938259999999","9.765625","0","26324","0","0","3.234375","14.046875","10","194","0","0" +"1","block","186","2026-09-29T01:53:13.0138184Z","186.0069921","1.000992","5.2734375","0","26324","0","0","3.234375","14.046875","10","194","0","0" +"1","block","187","2026-09-29T01:53:14.0130414Z","187.0063104","0.999318299999999","12.109375","0","26324","0","0","3.234375","14.046875","10","194","0","0" +"1","block","188","2026-09-29T01:53:15.0157458Z","188.0088621","1.0025517","6.0546875","0","26324","0","0","3.234375","14.046875","10","194","0","0" +"1","block","189","2026-09-29T01:53:16.0147116Z","189.0079258","0.999063699999994","6.25","0","26324","0","0","3.234375","14.046875","10","194","0","0" +"1","block","190","2026-09-29T01:53:17.0140264Z","190.0071512","0.9992254","5.859375","0","26324","0","0","3.234375","14.046875","10","194","0","0" +"1","block","191","2026-09-29T01:53:18.0196288Z","191.0129702","1.005819","7.03125","0","26324","0","0","3.234375","14.05078125","10","194","0","0" +"1","block","192","2026-09-29T01:53:19.0204907Z","192.0138244","1.00085420000002","9.9609375","0","26324","0","0","3.234375","14.05078125","10","194","0","0" +"1","block","193","2026-09-29T01:53:20.0082172Z","193.001536","0.987711599999983","6.34920634920635","0","26324","0","0","3.234375","14.05078125","10","194","0","0" +"1","block","194","2026-09-29T01:53:21.0150954Z","194.0083365","1.0068005","5.76923076923077","0.193993248910783","26324","0","0","3.234375","14.05078125","10","194","0","0" +"1","block","195","2026-09-29T01:53:22.0150327Z","195.0081919","0.999855400000001","7.6171875","0","26324","0","0","3.234375","14.05078125","10","194","0","0" +"1","block","196","2026-09-29T01:53:23.0148271Z","196.008068","0.999876099999994","5.078125","0","26324","0","0","3.234375","14.05078125","10","194","0","0" +"1","block","197","2026-09-29T01:53:24.0214002Z","197.0140972","1.0060292","18.5546875","0","26324","0","0","3.234375","14.05078125","10","194","0","0" +"1","block","198","2026-09-29T01:53:25.0106380Z","198.0039441","0.989846900000003","6.34920634920635","0","26324","0","0","3.234375","14.05078125","10","194","0","0" +"1","block","199","2026-09-29T01:53:26.0100896Z","199.0034864","0.999542300000002","10.15625","0.195401935465862","26324","0","0","3.234375","14.05078125","10","194","0","0" +"1","block","200","2026-09-29T01:53:27.0089672Z","200.0021454","0.998659000000004","6.640625","0.195574765760885","26324","0","0","3.234375","14.0546875","10","194","0","0" +"1","block","201","2026-09-29T01:53:28.0074065Z","201.0006451","0.998499699999996","16.6015625","0.195605967633241","26324","0.015625","0.195605967633241","3.234375","14.0546875","10","194","0","0" +"1","block","202","2026-09-29T01:53:29.0078764Z","202.0013328","1.00068770000001","11.71875","0","26324","0","0","3.234375","14.0546875","10","194","0","0" +"1","block","203","2026-09-29T01:53:30.0111519Z","203.0045368","1.00320399999998","13.6538461538462","0.194688717349615","26324","0","0","3.234375","14.0546875","10","194","0","0" +"1","block","204","2026-09-29T01:53:31.0127810Z","204.0061973","1.00166050000001","9.9609375","0","26324","0","0","3.234375","14.0546875","10","194","0","0" +"1","block","205","2026-09-29T01:53:32.0140803Z","205.0072901","1.00109279999998","10.15625","0","26324","0","0","3.234375","14.0546875","10","194","0","0" +"1","block","206","2026-09-29T01:53:33.0123709Z","206.0056271","0.998337000000021","6.25","0","26324","0","0","3.234375","14.0546875","10","194","0","0" +"1","block","207","2026-09-29T01:53:34.0126541Z","207.0057774","1.0001503","13.0859375","0","26324","0","0","3.2109375","14.03125","10","194","0","0" +"1","block","208","2026-09-29T01:53:35.0126680Z","208.0060081","1.0002307","5.6640625","0","26324","0","0","3.2109375","14.03125","10","194","0","0" +"1","block","209","2026-09-29T01:53:36.0166735Z","209.0099457","1.0039376","7.03125","0","26324","0","0","3.2109375","14.03125","10","194","0","0" +"1","block","210","2026-09-29T01:53:37.0211328Z","210.0144482","1.0045025","5.46875","0","26324","0","0","3.2109375","14.03125","10","194","0","0" +"1","block","211","2026-09-29T01:53:38.0177706Z","211.0113793","0.996931099999983","13.4765625","0","26324","0","0","3.2109375","14.03125","10","194","0","0" +"1","block","212","2026-09-29T01:53:39.0076790Z","212.0004865","0.989107200000007","4.76190476190476","0","26324","0","0","3.2109375","14.03125","10","194","0","0" +"1","block","213","2026-09-29T01:53:40.0198005Z","213.0132015","1.01271499999999","5.57692307692308","0","26324","0","0","3.21484375","14.03515625","10","194","0","0" +"1","block","214","2026-09-29T01:53:41.0157359Z","214.0089016","0.995700100000022","6.0546875","0","26324","0","0","3.21484375","14.03515625","10","194","0","0" +"1","block","215","2026-09-29T01:53:42.0111035Z","215.004367","0.9954654","17.7734375","0","26324","0","0","3.234375","14.0390625","10","194","0","0" +"1","block","216","2026-09-29T01:53:43.0185596Z","216.01237","1.008003","7.03125","0","26324","0","0","3.234375","14.0390625","10","194","0","0" +"1","block","217","2026-09-29T01:53:44.0170858Z","217.0103516","0.997981599999974","8.41487279843444","0","26324","0","0","3.234375","14.0390625","10","194","0","0" +"1","block","218","2026-09-29T01:53:45.0140854Z","218.0072388","0.996887200000003","5.45808966861598","0","26324","0","0","3.234375","14.0390625","10","194","0","0" +"1","block","219","2026-09-29T01:53:46.0160617Z","219.0093675","1.00212870000001","6.640625","0","26324","0","0","3.234375","14.0390625","10","194","0","0" +"1","block","220","2026-09-29T01:53:47.0147102Z","220.0080856","0.998718099999991","6.8359375","0","26324","0","0","3.234375","14.0390625","10","194","0","0" +"1","block","221","2026-09-29T01:53:48.0166687Z","221.0098468","1.0017612","5.6640625","0","26324","0","0","3.234375","14.0390625","10","194","0","0" +"1","block","222","2026-09-29T01:53:49.0186849Z","222.0118809","1.0020341","6.25","0","26324","0","0","3.234375","14.04296875","10","194","0","0" +"1","block","223","2026-09-29T01:53:50.0216457Z","223.0150388","1.00315789999999","6.8359375","0","26324","0","0","3.234375","14.046875","10","194","0","0" +"1","block","224","2026-09-29T01:53:51.0191788Z","224.0125017","0.997462900000016","3.90625","0","26324","0","0","3.234375","14.046875","10","194","0","0" +"1","block","225","2026-09-29T01:53:52.0094397Z","225.0026111","0.990109399999994","6.34920634920635","0","26324","0","0","3.234375","14.046875","10","194","0","0" +"1","block","226","2026-09-29T01:53:53.0141687Z","226.0074465","1.00483539999999","10.3846153846154","0.194372630582085","26324","0.015625","0.194372630582085","3.234375","14.046875","10","194","0","0" +"1","block","227","2026-09-29T01:53:54.0210768Z","227.0142487","1.00680220000001","9.1796875","0","26324","0","0","3.234375","14.046875","10","194","0","0" +"1","block","228","2026-09-29T01:53:55.0116772Z","228.0049133","0.990664600000002","11.3502935420744","0","26324","0","0","3.234375","14.046875","10","194","0","0" +"1","block","229","2026-09-29T01:53:56.0125591Z","229.0059226","1.00100929999999","9.55165692007797","0","26324","0","0","3.234375","14.046875","10","194","0","0" +"1","block","230","2026-09-29T01:53:57.0155944Z","230.0088738","1.00295120000001","5.2734375","0","26324","0","0","3.234375","14.046875","10","194","0","0" +"1","block","231","2026-09-29T01:53:58.0146433Z","231.0078632","0.998989399999999","8.3984375","0","26324","0","0","3.234375","14.046875","10","194","0","0" +"1","block","232","2026-09-29T01:53:59.0183927Z","232.011299","1.00343579999998","6.4453125","0","26324","0","0","3.234375","14.046875","10","194","0","0" +"1","block","233","2026-09-29T01:54:00.0144728Z","233.0077027","0.996403700000002","38.0859375","0","26324","0","0","3.234375","14.05078125","10","194","0","0" +"1","block","234","2026-09-29T01:54:01.0116236Z","234.0043652","0.996662500000014","29.6875","0","26324","0","0","3.234375","14.05078125","10","194","0","0" +"1","block","235","2026-09-29T01:54:02.0162788Z","235.0095751","1.00520989999998","10.546875","0","26324","0","0","3.203125","14.03515625","9","192","0","0" +"1","block","236","2026-09-29T01:54:03.0178400Z","236.0112927","1.00171760000001","5.078125","0","26324","0","0","3.203125","14.03515625","9","192","0","0" +"1","block","237","2026-09-29T01:54:04.0215305Z","237.0148292","1.0035365","8.203125","0","26324","0","0","3.23046875","14.04296875","10","192","0","0" +"1","block","238","2026-09-29T01:54:05.0076272Z","238.0007646","0.985935400000017","5.95238095238095","0","26324","0","0","3.23046875","14.04296875","10","192","0","0" +"1","block","239","2026-09-29T01:54:06.0167698Z","239.0100284","1.00926379999999","9.42307692307692","0.193519771540407","26324","0","0","3.23046875","14.04296875","10","192","0","0" +"1","block","240","2026-09-29T01:54:07.0168159Z","240.0039071","0.99387870000001","30.1369863013699","0","26324","0","0","3.23046875","14.04296875","10","192","0","0" +"1","block","241","2026-09-29T01:54:08.0230175Z","241.0166629","1.01275580000001","29.2397660818713","0","26324","0","0","3.23046875","14.04296875","10","192","0","0" +"1","block","242","2026-09-29T01:54:09.0185213Z","242.0119019","0.995238999999998","11.328125","0.196246831163168","26324","0","0","3.23046875","14.04296875","10","192","0","0" +"1","block","243","2026-09-29T01:54:10.0162586Z","243.0094773","0.997575399999988","9.5703125","0","26324","0","0","3.23046875","14.04296875","10","192","0","0" +"1","block","244","2026-09-29T01:54:11.0153199Z","244.0086564","0.999179099999992","6.0546875","0","26324","0","0","3.23046875","14.04296875","10","192","0","0" +"1","block","245","2026-09-29T01:54:12.0180273Z","245.011282","1.00262560000002","7.2265625","0","26324","0","0","3.23046875","14.046875","10","192","0","0" +"1","block","246","2026-09-29T01:54:13.0160941Z","246.0092505","0.997968499999985","13.4765625","0","26324","0","0","3.23046875","14.046875","10","192","0","0" +"1","block","247","2026-09-29T01:54:14.0121636Z","247.0053733","0.996122800000023","7.03125","0","26324","0","0","3.23046875","14.05078125","10","192","0","0" +"1","block","248","2026-09-29T01:54:15.0125953Z","248.0058004","1.0004271","8.59375","0","26324","0","0","3.23046875","14.05078125","10","192","0","0" +"1","block","249","2026-09-29T01:54:16.0184202Z","249.0116215","1.00582109999999","5.859375","0","26324","0","0","3.23046875","14.05078125","10","192","0","0" +"1","block","250","2026-09-29T01:54:17.0176599Z","250.0107704","0.999148899999994","6.25","0","26324","0","0","3.23046875","14.05078125","10","192","0","0" +"1","block","251","2026-09-29T01:54:18.0162908Z","251.0095444","0.998773999999997","6.4453125","0","26324","0","0","3.23046875","14.05078125","10","192","0","0" +"1","block","252","2026-09-29T01:54:19.0175068Z","252.0107135","1.0011691","5.6640625","0","26324","0","0","3.23046875","14.05078125","10","192","0","0" +"1","block","253","2026-09-29T01:54:20.0326456Z","253.0258839","1.01517040000002","5.96153846153846","0","26324","0","0","3.23046875","14.05078125","10","192","0","0" +"1","block","254","2026-09-29T01:54:21.0180995Z","254.0112491","0.98536519999999","5.55555555555556","0","26324","0","0","3.23046875","14.0546875","10","192","0","0" +"1","block","255","2026-09-29T01:54:22.0204024Z","255.0136977","1.00244860000001","5.2734375","0","26324","0","0","3.23046875","14.0546875","10","192","0","0" +"1","block","256","2026-09-29T01:54:23.0206574Z","256.0139168","1.00021910000001","5.6640625","0","26324","0","0","3.23046875","14.0546875","10","192","0","0" +"1","block","257","2026-09-29T01:54:24.0215091Z","257.0146489","1.00073209999999","5.6640625","0","26324","0","0","3.23046875","14.0546875","10","192","0","0" +"1","block","258","2026-09-29T01:54:25.0197878Z","258.012945","0.998296100000005","8.984375","0","26324","0","0","3.23046875","14.0546875","10","192","0","0" +"1","block","259","2026-09-29T01:54:26.0179784Z","259.0113306","0.998385600000006","5.078125","0","26324","0","0","3.23046875","14.0546875","10","192","0","0" +"1","block","260","2026-09-29T01:54:27.0151015Z","260.0082653","0.996934699999997","5.46875","0","26324","0","0","3.23046875","14.0546875","10","192","0","0" +"1","block","261","2026-09-29T01:54:28.0153833Z","261.008569","1.00030369999996","5.46875","0","26324","0","0","3.23046875","14.0546875","10","192","0","0" +"1","block","262","2026-09-29T01:54:29.0157599Z","262.0091115","1.00054249999999","7.6171875","0","26324","0","0","3.23046875","14.0546875","10","192","0","0" +"1","block","263","2026-09-29T01:54:30.0219607Z","263.0153151","1.00620360000005","7.2265625","0","26324","0","0","3.23046875","14.0546875","10","192","0","0" +"1","block","264","2026-09-29T01:54:31.0078514Z","264.0011142","0.985799099999952","6.54761904761905","0","26324","0","0","3.23046875","14.0546875","10","192","0","0" +"1","block","265","2026-09-29T01:54:32.0196656Z","265.0130011","1.01188690000004","5.38461538461539","0","26324","0","0","3.23046875","14.0546875","10","192","0","0" +"1","block","266","2026-09-29T01:54:33.0191191Z","266.0123679","0.999366800000018","4.1015625","0","26324","0","0","3.23046875","14.0546875","10","192","0","0" +"1","block","267","2026-09-29T01:54:34.0178138Z","267.0111006","0.998732699999948","6.25","0","26324","0","0","3.31640625","14.24609375","13","194","0","0" +"1","block","268","2026-09-29T01:54:35.0101442Z","268.0035043","0.992403700000011","11.9047619047619","0","26324","0","0","3.31640625","14.24609375","13","194","0","0" +"1","block","269","2026-09-29T01:54:36.0099848Z","269.0031678","0.999663499999997","14.0625","0.195378244779369","26324","0","0","3.31640625","14.24609375","13","194","0","0" +"1","block","270","2026-09-29T01:54:37.0119785Z","270.00526","1.00209219999999","7.5","0.194904720344097","26324","0","0","3.31640625","14.24609375","13","194","0","0" +"1","block","271","2026-09-29T01:54:38.0120387Z","271.0053304","1.00007040000003","9.9609375","0","26324","0","0","3.31640625","14.24609375","13","194","0","0" +"1","block","272","2026-09-29T01:54:39.0073644Z","272.0006048","0.995274399999971","11.9047619047619","0","26324","0","0","3.3359375","14.25","13","194","0","0" +"1","block","273","2026-09-29T01:54:40.0175873Z","273.0108277","1.01022290000003","9.61538461538461","0","26324","0","0","3.3359375","14.25","13","194","0","0" +"1","block","274","2026-09-29T01:54:41.0195103Z","274.0128971","1.00206939999998","7.03125","0","26324","0","0","3.3359375","14.25","13","194","0","0" +"1","block","275","2026-09-29T01:54:42.0136035Z","275.0069188","0.994021700000019","7.8125","0","26324","0","0","3.3359375","14.25","13","194","0","0" +"1","block","276","2026-09-29T01:54:43.0201958Z","276.0134221","1.00650330000002","7.03125","0","26324","0","0","3.3359375","14.25","13","194","0","0" +"1","block","277","2026-09-29T01:54:44.0197933Z","277.0132349","0.999812799999972","3.90625","0","26324","0","0","3.3359375","14.25390625","13","194","0","0" +"1","block","278","2026-09-29T01:54:45.0184959Z","278.0117769","0.998541999999986","10.3515625","0","26324","0","0","3.3359375","14.2578125","13","194","0","0" +"1","block","279","2026-09-29T01:54:46.0189668Z","279.0121767","1.00039980000003","6.8359375","0","26324","0","0","3.3359375","14.2578125","13","194","0","0" +"1","block","280","2026-09-29T01:54:47.0179619Z","280.0118633","0.999686599999961","5.2734375","0","26324","0","0","3.3359375","14.26171875","13","194","0","0" +"1","block","281","2026-09-29T01:54:48.0221755Z","281.015455","1.00359170000002","5.859375","0","26324","0","0","3.3359375","14.265625","13","194","0","0" +"1","block","282","2026-09-29T01:54:49.0071782Z","282.0004493","0.984994300000039","6.34920634920635","0","26324","0","0","3.3359375","14.265625","13","194","0","0" +"1","block","283","2026-09-29T01:54:50.0104204Z","283.0037191","1.0032698","9.375","0.194675948583323","26324","0.015625","0.194675948583323","3.3359375","14.265625","13","194","0","0" +"1","block","284","2026-09-29T01:54:51.0127547Z","284.0061244","1.00240529999996","9.03846153846154","0.194843842106588","26324","0","0","3.3359375","14.265625","13","194","0","0" +"1","block","285","2026-09-29T01:54:52.0162198Z","285.0088923","1.00276790000004","4.8828125","0","26324","0","0","3.3359375","14.265625","13","194","0","0" +"1","block","286","2026-09-29T01:54:53.0132190Z","286.0065234","0.997631099999978","5.6640625","0","26324","0","0","3.3359375","14.265625","13","194","0","0" +"1","block","287","2026-09-29T01:54:54.0118872Z","287.0050945","0.998571099999992","8.0078125","0","26324","0","0","3.3359375","14.265625","13","194","0","0" +"1","block","288","2026-09-29T01:54:55.0091671Z","288.0025651","0.997470599999986","28.3730158730159","0","26324","0","0","3.3359375","14.265625","13","194","0","0" +"1","block","289","2026-09-29T01:54:56.0080015Z","289.0014491","0.998884000000032","9.5703125","0.195530712274892","26324","0.015625","0.195530712274892","3.3359375","14.265625","13","194","0","0" +"1","block","290","2026-09-29T01:54:57.0104125Z","290.0036108","1.00216169999999","15.0390625","0.194891203684997","26324","0","0","3.3359375","14.265625","13","194","0","0" +"1","block","291","2026-09-29T01:54:58.0208331Z","291.0140577","1.01044689999998","7.69230769230769","0.193293185421227","26324","0","0","3.359375","14.26953125","13","194","0","0" +"1","block","292","2026-09-29T01:54:59.0221212Z","292.0149807","1.000923","6.640625","0","26324","0","0","3.359375","14.26953125","13","194","0","0" +"1","block","293","2026-09-29T01:55:00.0059016Z","292.9990447","0.984064000000046","6.54761904761905","0","26324","0","0","3.359375","14.26953125","13","194","0","0" +"1","block","294","2026-09-29T01:55:01.0098050Z","294.0029573","1.00391259999998","7.03125","0","26324","0","0","3.359375","14.26953125","13","194","0","0" +"1","block","295","2026-09-29T01:55:02.0102771Z","295.0036966","1.00073930000002","13.4765625","0.19516821214076","26324","0","0","3.359375","14.26953125","13","194","0","0" +"1","block","296","2026-09-29T01:55:03.0089979Z","296.0021633","0.998466699999994","10.546875","0.195612432542819","26324","0","0","3.359375","14.26953125","13","194","0","0" +"1","block","297","2026-09-29T01:55:04.0089192Z","297.0021314","0.99996809999999","9.1796875","0.19531873066751","26324","0","0","3.31640625","14.24609375","13","194","0","0" +"1","block","298","2026-09-29T01:55:05.0107290Z","298.0038884","1.001757","17.1875","0.194969937819252","26324","0","0","3.31640625","14.24609375","13","194","0","0" +"1","block","299","2026-09-29T01:55:06.0141624Z","299.0074673","1.00357889999998","8.26923076923077","0.194615988837553","26324","0","0","3.31640625","14.24609375","13","194","0","0" +"1","block","300","2026-09-29T01:55:07.0167435Z","300.0099228","1.0024555","4.8828125","0","26324","0","0","3.31640625","14.24609375","13","194","0","0" diff --git a/docs/benchmarks/2026-09-29-v0.2.0/results/run-2-allow-end.json b/docs/benchmarks/2026-09-29-v0.2.0/results/run-2-allow-end.json new file mode 100644 index 0000000..14f27af --- /dev/null +++ b/docs/benchmarks/2026-09-29-v0.2.0/results/run-2-allow-end.json @@ -0,0 +1,12 @@ +{ + "state": "allow", + "layers": { + "shutdown": false, + "local_shutdown": false, + "update": false, + "remote": false, + "sleep": false + }, + "uptime_seconds": 333, + "blocked_count": 0 +} diff --git a/docs/benchmarks/2026-09-29-v0.2.0/results/run-2-allow-start.json b/docs/benchmarks/2026-09-29-v0.2.0/results/run-2-allow-start.json new file mode 100644 index 0000000..d41852d --- /dev/null +++ b/docs/benchmarks/2026-09-29-v0.2.0/results/run-2-allow-start.json @@ -0,0 +1,12 @@ +{ + "state": "allow", + "layers": { + "shutdown": false, + "local_shutdown": false, + "update": false, + "remote": false, + "sleep": false + }, + "uptime_seconds": 32, + "blocked_count": 0 +} diff --git a/docs/benchmarks/2026-09-29-v0.2.0/results/run-2-allow.csv b/docs/benchmarks/2026-09-29-v0.2.0/results/run-2-allow.csv new file mode 100644 index 0000000..7503b19 --- /dev/null +++ b/docs/benchmarks/2026-09-29-v0.2.0/results/run-2-allow.csv @@ -0,0 +1,301 @@ +"run","scenario","sample","utc","elapsed_s","interval_s","system_cpu_pct","collector_cpu_pct","pid","process_cpu_s","cpu_pct","private_mib","working_set_mib","threads","handles","io_read_bytes_s","io_write_bytes_s" +"2","allow","1","2026-09-29T01:55:41.1597410Z","1.0138575","1.0138529","7.88461538461539","0","18792","0","0","2.04296875","12.6171875","8","176","0","0" +"2","allow","2","2026-09-29T01:55:42.1508300Z","2.0048799","0.9910224","12.1031746031746","0","18792","0.015625","0.19708182176306","2.04296875","12.6171875","8","176","0","0" +"2","allow","3","2026-09-29T01:55:43.1554007Z","3.0094434","1.0045635","9.42307692307692","0.194425240415364","18792","0","0","2.04296875","12.6171875","8","176","0","0" +"2","allow","4","2026-09-29T01:55:44.1558182Z","4.0099261","1.0004827","5.2734375","0","18792","0","0","2.04296875","12.6171875","8","176","0","0" +"2","allow","5","2026-09-29T01:55:45.1526441Z","5.0065657","0.9966396","7.8125","0","18792","0","0","2.04296875","12.6171875","8","176","0","0" +"2","allow","6","2026-09-29T01:55:46.1526010Z","6.0067248","1.0001591","11.1328125","0","18792","0","0","2.04296875","12.6171875","8","176","0","0" +"2","allow","7","2026-09-29T01:55:47.1522362Z","7.0062575","0.9995327","6.25","0","18792","0","0","2.04296875","12.6171875","8","176","0","0" +"2","allow","8","2026-09-29T01:55:48.1534674Z","8.0074707","1.0012132","10.7421875","0","18792","0","0","2.04296875","12.6171875","8","176","0","0" +"2","allow","9","2026-09-29T01:55:49.1584820Z","9.0123148","1.0048441","6.8359375","0","18792","0","0","2.04296875","12.6171875","8","176","0","0" +"2","allow","10","2026-09-29T01:55:50.1580284Z","10.0121871","0.9998723","8.59375","0","18792","0","0","2.04296875","12.6171875","8","176","0","0" +"2","allow","11","2026-09-29T01:55:51.1608738Z","11.0148499","1.0026628","7.03125","0","18792","0","0","2.04296875","12.6171875","8","176","0","0" +"2","allow","12","2026-09-29T01:55:52.1592806Z","12.0133207","0.9984708","5.078125","0","18792","0","0","2.04296875","12.6171875","8","176","0","0" +"2","allow","13","2026-09-29T01:55:53.1488296Z","13.0031043","0.989783599999999","5.95238095238095","0","18792","0","0","2.04296875","12.6171875","8","176","0","0" +"2","allow","14","2026-09-29T01:55:54.1618868Z","14.0159025","1.0127982","10","0.192844438309626","18792","0","0","2.04296875","12.6171875","8","176","0","0" +"2","allow","15","2026-09-29T01:55:55.1616906Z","15.0158512","0.999948700000001","5.2734375","0","18792","0","0","2.04296875","12.6171875","8","176","0","0" +"2","allow","16","2026-09-29T01:55:56.1601516Z","16.014194","0.9983428","8.984375","0","18792","0","0","2.04296875","12.6171875","8","176","0","0" +"2","allow","17","2026-09-29T01:55:57.1597358Z","17.0138637","0.999669699999998","6.25","0","18792","0","0","2.04296875","12.6171875","8","176","0","0" +"2","allow","18","2026-09-29T01:55:58.1621085Z","18.0163541","1.0024904","7.8125","0","18792","0","0","2.04296875","12.6171875","8","176","0","0" +"2","allow","19","2026-09-29T01:55:59.1494402Z","19.00345","0.9870959","5.55555555555556","0","18792","0","0","2.04296875","12.6171875","8","176","0","0" +"2","allow","20","2026-09-29T01:56:00.1515057Z","20.0055328","1.0020828","7.6171875","0.194906548640492","18792","0","0","2.04296875","12.6171875","8","176","0","0" +"2","allow","21","2026-09-29T01:56:01.1482045Z","21.002346","0.996813200000002","7.8125","0.195936911750366","18792","0","0","2.04296875","12.6171875","8","176","0","0" +"2","allow","22","2026-09-29T01:56:02.1499629Z","22.0038952","1.0015492","8.59375","0","18792","0","0","2.04296875","12.6171875","8","176","0","0" +"2","allow","23","2026-09-29T01:56:03.1477330Z","23.0018673","0.997972100000002","9.1796875","0.195709379049775","18792","0","0","2.04296875","12.6171875","8","176","0","0" +"2","allow","24","2026-09-29T01:56:04.1485416Z","24.0025369","1.0006696","5.2734375","0","18792","0","0","2.04296875","12.6171875","8","176","0","0" +"2","allow","25","2026-09-29T01:56:05.1535730Z","25.0075326","1.0049957","9.80769230769231","0.194341627531342","18792","0","0","2.04296875","12.6171875","8","176","0","0" +"2","allow","26","2026-09-29T01:56:06.1528844Z","26.0069271","0.999394500000001","8.984375","0","18792","0","0","2.04296875","12.6171875","8","176","0","0" +"2","allow","27","2026-09-29T01:56:07.1503358Z","27.0044125","0.997485399999999","10.3174603174603","0","18792","0","0","2.04296875","12.6171875","8","176","0","0" +"2","allow","28","2026-09-29T01:56:08.1491049Z","28.0031996","0.998787100000001","6.4453125","0.195549682209552","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","29","2026-09-29T01:56:09.1529527Z","29.0069391","1.0037395","8.84615384615385","0.194584849953598","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","30","2026-09-29T01:56:10.1560393Z","30.0103754","1.0034363","7.6171875","0","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","31","2026-09-29T01:56:11.1550646Z","31.0091623","0.998786900000002","7.85854616895874","0","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","32","2026-09-29T01:56:12.1530649Z","32.0070872","0.997924900000001","7.57281553398058","0","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","33","2026-09-29T01:56:13.1578098Z","33.011947","1.0048598","7.2265625","0","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","34","2026-09-29T01:56:14.1585517Z","34.0125131","1.0005661","33.3984375","0","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","35","2026-09-29T01:56:15.1553451Z","35.0095376","0.997024500000002","20.7436399217221","0","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","36","2026-09-29T01:56:16.1516404Z","36.00563","0.996092399999995","20.0392927308448","0.196078697116855","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","37","2026-09-29T01:56:17.1477128Z","37.0018049","0.9961749","8.26771653543307","0","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","38","2026-09-29T01:56:18.1615796Z","38.0157228","1.0139179","9.80769230769231","0.192631474402414","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","39","2026-09-29T01:56:19.1469520Z","39.0009396","0.985216799999996","6.54761904761905","0","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","40","2026-09-29T01:56:20.1492744Z","40.0032668","1.0023272","8.203125","0","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","41","2026-09-29T01:56:21.1537357Z","41.0077667","1.0044999","7.69230769230769","0.194437550466655","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","42","2026-09-29T01:56:22.1513816Z","42.0053592","0.997592500000003","14.0873015873016","0","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","43","2026-09-29T01:56:23.1496352Z","43.0037161","0.998356899999997","9.9609375","0.195633946136898","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","44","2026-09-29T01:56:24.1512260Z","44.0054399","1.0017238","8.38206627680312","0.194976399682228","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","45","2026-09-29T01:56:25.1504676Z","45.0046411","0.999201200000002","15.6555772994129","0","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","46","2026-09-29T01:56:26.1488678Z","46.0030159","0.998374800000001","14.6484375","0.195630438588794","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","47","2026-09-29T01:56:27.1509809Z","47.0051177","1.0021018","7.03125","0.19490285318318","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","48","2026-09-29T01:56:28.1530223Z","48.0070701","1.0019524","4.23076923076923","0.194931914929292","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","49","2026-09-29T01:56:29.1525699Z","49.0066702","0.999600099999995","10.3515625","0","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","50","2026-09-29T01:56:30.1561996Z","50.0103636","1.0036934","8.0078125","0","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","51","2026-09-29T01:56:31.1516351Z","51.0055749","0.995211300000001","23.7254901960784","0.196252293357199","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","52","2026-09-29T01:56:32.1494882Z","52.0035252","0.997950299999999","20.5533596837945","0","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","53","2026-09-29T01:56:33.1529178Z","53.0069854","1.0034602","16.3461538461538","0.194639010097262","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","54","2026-09-29T01:56:34.1578159Z","54.0119671","1.0049817","10.7421875","0","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","55","2026-09-29T01:56:35.1479782Z","55.002413","0.990445899999997","3.96825396825397","0","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","56","2026-09-29T01:56:36.1467319Z","56.0008954","0.9984824","10.3515625","0.195609356759819","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","57","2026-09-29T01:56:37.1596526Z","57.0137987","1.0129033","4.42307692307692","0","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","58","2026-09-29T01:56:38.1591387Z","58.0131661","0.999367400000004","7.6171875","0","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","59","2026-09-29T01:56:39.1584502Z","59.0125607","0.999394599999995","5.46875","0","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","60","2026-09-29T01:56:40.1580408Z","60.0122779","0.999717199999999","8.7890625","0","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","61","2026-09-29T01:56:41.1590668Z","61.0131955","1.0009176","6.0546875","0","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","62","2026-09-29T01:56:42.1568288Z","62.0107948","0.997599300000005","8.203125","0","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","63","2026-09-29T01:56:43.1594430Z","63.013007","1.0022122","6.0546875","0","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","64","2026-09-29T01:56:44.1533425Z","64.0075233","0.994516300000008","10.7421875","0","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","65","2026-09-29T01:56:45.1547814Z","65.0087531","1.00122979999999","26.3671875","0","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","66","2026-09-29T01:56:46.1543613Z","66.0084322","0.999679100000009","9.765625","0","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","67","2026-09-29T01:56:47.1526658Z","67.0066252","0.998193000000001","8.203125","0","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","68","2026-09-29T01:56:48.1508142Z","68.0048205","0.998195299999992","13.8888888888889","0","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","69","2026-09-29T01:56:49.1485968Z","69.0028436","0.998023099999997","10.3515625","0.195699378100568","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","70","2026-09-29T01:56:50.1488508Z","70.0028886","1.000045","6.8359375","0.19530371133299","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","71","2026-09-29T01:56:51.1536636Z","71.0078288","1.00494020000001","8.46153846153846","0.194352360468811","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","72","2026-09-29T01:56:52.1510979Z","72.0051655","0.997336699999991","8.94632206759443","0","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","73","2026-09-29T01:56:53.1562801Z","73.0102686","1.00510310000001","6.71785028790787","0.194320861213141","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","74","2026-09-29T01:56:54.1547316Z","74.0087887","0.998520099999993","7.2265625","0","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","75","2026-09-29T01:56:55.1572073Z","75.0112212","1.0024325","6.640625","0","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","76","2026-09-29T01:56:56.1595289Z","76.0137797","1.00255850000001","9.5703125","0","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","77","2026-09-29T01:56:57.1489456Z","77.003138","0.989358299999992","5.35714285714286","0","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","78","2026-09-29T01:56:58.1609680Z","78.0149924","1.0118544","5.96153846153846","0.193024312588846","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","79","2026-09-29T01:56:59.1481458Z","79.0023443","0.987351899999993","6.34920634920635","0","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","80","2026-09-29T01:57:00.1513353Z","80.0048368","1.0024925","15.4296875","0.194826893966787","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","81","2026-09-29T01:57:01.1500420Z","81.0042349","0.999398100000008","9.5703125","0","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","82","2026-09-29T01:57:02.1495848Z","82.0036799","0.999444999999994","8.203125","0.195420958632042","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","83","2026-09-29T01:57:03.1489525Z","83.0029974","0.999317500000004","7.6171875","0.195445891821167","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","84","2026-09-29T01:57:04.1580787Z","84.0122889","1.0092915","8.84615384615385","0.193514460391274","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","85","2026-09-29T01:57:05.1505954Z","85.0047017","0.992412799999997","7.93650793650794","0","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","86","2026-09-29T01:57:06.1495118Z","86.0035615","0.998859799999991","18.5546875","0.195535449519544","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","87","2026-09-29T01:57:07.1490279Z","87.0031812","0.999619700000011","8.984375","0.195386805602168","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","88","2026-09-29T01:57:08.1546147Z","88.0086968","1.0055156","10.5769230769231","0.194241143548644","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","89","2026-09-29T01:57:09.1531179Z","89.0072557","0.998558900000006","5.6640625","0","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","90","2026-09-29T01:57:10.1515855Z","90.0056838","0.998428099999998","10.6508875739645","0","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","91","2026-09-29T01:57:11.1530939Z","91.0071563","1.00147249999999","14.8936170212766","0.195025325208632","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","92","2026-09-29T01:57:12.1571982Z","92.0114334","1.00427710000001","5.46875","0","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","93","2026-09-29T01:57:13.1568738Z","93.0109966","0.999563199999997","8.984375","0","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","94","2026-09-29T01:57:14.1533318Z","94.007345","0.996348400000002","7.6171875","0","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","95","2026-09-29T01:57:15.1541590Z","95.0082882","1.00094319999999","6.25","0","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","96","2026-09-29T01:57:16.1577011Z","96.0116275","1.00333929999999","8.3984375","0","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","97","2026-09-29T01:57:17.1584558Z","97.012462","1.00083450000001","10.7421875","0","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","98","2026-09-29T01:57:18.1463143Z","98.0003824","0.987920399999993","6.74603174603175","0","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","99","2026-09-29T01:57:19.1489378Z","99.002973","1.0025906","6.0546875","0","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","100","2026-09-29T01:57:20.1534354Z","100.0074675","1.00449449999999","8.65384615384615","0.194438595731486","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","101","2026-09-29T01:57:21.1522007Z","101.0057995","0.998332000000005","10.546875","0","18792","0","0","2.015625","12.6015625","7","176","0","0" +"2","allow","102","2026-09-29T01:57:22.1576829Z","102.0116326","1.0058331","10.3515625","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","103","2026-09-29T01:57:23.1544071Z","103.0084574","0.996824799999999","6.0546875","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","104","2026-09-29T01:57:24.1559208Z","104.0101353","1.0016779","4.296875","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","105","2026-09-29T01:57:25.1529527Z","105.0071301","0.996994799999996","7.03125","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","106","2026-09-29T01:57:26.1543309Z","106.0083381","1.00120799999999","8.59375","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","107","2026-09-29T01:57:27.1549725Z","107.0089678","1.0006297","7.6171875","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","108","2026-09-29T01:57:28.1552026Z","108.0092389","1.00027110000001","5.46875","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","109","2026-09-29T01:57:29.1537650Z","109.0078126","0.998573699999994","7.8125","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","110","2026-09-29T01:57:30.1488630Z","110.0027731","0.994960500000005","12.3015873015873","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","111","2026-09-29T01:57:31.1489540Z","111.0030241","1.00025099999999","8.3984375","0.195263488864297","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","112","2026-09-29T01:57:32.1567533Z","112.0109065","1.0078824","7.5","0.193785009044706","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","113","2026-09-29T01:57:33.1467093Z","113.0008414","0.989934900000009","5.35714285714286","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","114","2026-09-29T01:57:34.1480495Z","114.0021392","1.00129779999999","7.8125","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","115","2026-09-29T01:57:35.1475649Z","115.0015642","0.999425000000002","7.05882352941176","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","116","2026-09-29T01:57:36.1615844Z","116.0156348","1.01407060000001","5.74712643678161","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","117","2026-09-29T01:57:37.1480830Z","117.0022121","0.986577299999993","10.1190476190476","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","118","2026-09-29T01:57:38.1501642Z","118.0042326","1.0020205","8.59375","0.194918666833662","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","119","2026-09-29T01:57:39.1466708Z","119.0006789","0.996446300000002","9.765625","0.196009057387237","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","120","2026-09-29T01:57:40.1589446Z","120.0130819","1.01240299999999","9.42307692307692","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","121","2026-09-29T01:57:41.1466286Z","121.0005954","0.987513500000006","6.34920634920635","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","122","2026-09-29T01:57:42.1479656Z","122.0019878","1.0013924","6.8359375","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","123","2026-09-29T01:57:43.1502308Z","123.00433","1.0023422","5.46875","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","124","2026-09-29T01:57:44.1517748Z","124.0058581","1.0015281","11.7307692307692","0.195014498345079","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","125","2026-09-29T01:57:45.1544804Z","125.0086458","1.0027877","9.375","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","126","2026-09-29T01:57:46.1550859Z","126.0092923","1.0006465","6.640625","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","127","2026-09-29T01:57:47.1564696Z","127.0101391","1.00084679999999","10.546875","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","128","2026-09-29T01:57:48.1508452Z","128.0050164","0.994877299999999","4.96031746031746","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","129","2026-09-29T01:57:49.1571746Z","129.0111839","1.0061675","7.5","0.194115293924719","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","130","2026-09-29T01:57:50.1584955Z","130.0125119","1.001328","8.3984375","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","131","2026-09-29T01:57:51.1616335Z","131.0153105","1.00279860000001","4.4921875","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","132","2026-09-29T01:57:52.1474139Z","132.0014972","0.98618669999999","7.73809523809524","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","133","2026-09-29T01:57:53.1502449Z","133.0042659","1.00276870000002","7.03125","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","134","2026-09-29T01:57:54.1535935Z","134.008172","1.00390609999999","14.2307692307692","0.194552558252212","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","135","2026-09-29T01:57:55.1516068Z","135.0057875","0.997615499999995","8.03921568627451","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","136","2026-09-29T01:57:56.1476710Z","136.0016431","0.995855599999999","9.48616600790514","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","137","2026-09-29T01:57:57.1563370Z","137.0106135","1.00897040000001","10.1923076923077","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","138","2026-09-29T01:57:58.1621885Z","138.0162181","1.0056046","6.640625","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","139","2026-09-29T01:57:59.1601128Z","139.0141637","0.99794559999998","5.078125","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","140","2026-09-29T01:58:00.1504229Z","140.0044985","0.990334799999999","8.33333333333333","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","141","2026-09-29T01:58:01.1531714Z","141.0071582","1.00265970000001","12.3076923076923","0.194794405320168","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","142","2026-09-29T01:58:02.1548270Z","142.0089445","1.00178629999999","6.25","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","143","2026-09-29T01:58:03.1555770Z","143.0097268","1.0007823","5.859375","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","144","2026-09-29T01:58:04.1595892Z","144.0138015","1.00407470000002","5.2734375","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","145","2026-09-29T01:58:05.1624861Z","145.0165976","1.00279609999998","5.6640625","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","146","2026-09-29T01:58:06.1485981Z","146.0027154","0.986117800000017","8.73015873015873","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","147","2026-09-29T01:58:07.1470709Z","147.0012908","0.998575399999993","8.7890625","0.195591139136816","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","148","2026-09-29T01:58:08.1476790Z","148.001862","1.0005712","5.46875","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","149","2026-09-29T01:58:09.1501053Z","149.0041771","1.0023151","8.203125","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","150","2026-09-29T01:58:10.1620126Z","150.0160641","1.011887","10.1923076923077","0.193018093917601","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","151","2026-09-29T01:58:11.1611342Z","151.0151679","0.9991038","5.46875","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","152","2026-09-29T01:58:12.1516016Z","152.0056449","0.990476999999998","18.5770750988142","0.197190343642508","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","153","2026-09-29T01:58:13.1551338Z","153.0092177","1.0035728","16.2162162162162","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","154","2026-09-29T01:58:14.1558405Z","154.0100298","1.00081209999999","7.82778864970646","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","155","2026-09-29T01:58:15.1558226Z","155.009981","0.999951199999998","4.87329434697856","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","156","2026-09-29T01:58:16.1517968Z","156.0058327","0.995851700000003","10.15625","0.19612608985856","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","157","2026-09-29T01:58:17.1478903Z","157.0020006","0.996167900000017","30.7539682539683","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","158","2026-09-29T01:58:18.1580592Z","158.012125","1.0101244","20.3846153846154","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","159","2026-09-29T01:58:19.1535329Z","159.0076272","0.995502200000004","46.09375","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","160","2026-09-29T01:58:20.1478960Z","160.0019202","0.994292999999999","16.468253968254","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","161","2026-09-29T01:58:21.1584305Z","161.0126287","1.01070849999999","8.26923076923077","0.193243155667536","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","162","2026-09-29T01:58:22.1606496Z","162.0148222","1.0021935","6.4453125","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","163","2026-09-29T01:58:23.1592001Z","163.0132334","0.998411199999993","6.0546875","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","164","2026-09-29T01:58:24.1489072Z","164.0029124","0.989678999999995","22.8174603174603","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","165","2026-09-29T01:58:25.1545445Z","165.0085265","1.0056141","22.6923076923077","0.194222117609528","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","166","2026-09-29T01:58:26.1475684Z","166.0017118","0.993185299999993","37.6984126984127","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","167","2026-09-29T01:58:27.1591312Z","167.0131577","1.01144590000001","15.1923076923077","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","168","2026-09-29T01:58:28.1481021Z","168.0022044","0.989046699999989","7.53968253968254","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","169","2026-09-29T01:58:29.1587448Z","169.012884","1.0106796","7.88461538461539","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","170","2026-09-29T01:58:30.1568116Z","170.0108201","0.997936100000004","4.8828125","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","171","2026-09-29T01:58:31.1564115Z","171.0105043","0.99968419999999","4.4921875","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","172","2026-09-29T01:58:32.1591158Z","172.0131361","1.00263180000002","6.4453125","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","173","2026-09-29T01:58:33.1577778Z","173.0118104","0.998674300000005","8.59375","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","174","2026-09-29T01:58:34.1540424Z","174.0081823","0.996371899999986","7.84313725490196","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","175","2026-09-29T01:58:35.1539666Z","175.0080992","0.999916900000017","4.28015564202335","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","176","2026-09-29T01:58:36.1572175Z","176.0113235","1.0032243","5.6640625","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","177","2026-09-29T01:58:37.1476033Z","177.0016818","0.990358299999997","7.34126984126984","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","178","2026-09-29T01:58:38.1613603Z","178.0154466","1.01376479999999","9.03846153846154","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","179","2026-09-29T01:58:39.1604617Z","179.0146968","0.999250200000006","6.0546875","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","180","2026-09-29T01:58:40.1578112Z","180.0118031","0.997106299999984","5.6640625","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","181","2026-09-29T01:58:41.1560569Z","181.0101992","0.998396100000008","11.1328125","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","182","2026-09-29T01:58:42.1554186Z","182.0094369","0.999237700000009","6.4453125","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","183","2026-09-29T01:58:43.1492594Z","183.0036421","0.994205199999982","24.8015873015873","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","184","2026-09-29T01:58:44.1580145Z","184.0119732","1.00833110000002","17.5","0.193698776126211","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","185","2026-09-29T01:58:45.1575465Z","185.0115391","0.999565899999993","7.8125","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","186","2026-09-29T01:58:46.1540256Z","186.0080816","0.996542500000004","6.8359375","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","187","2026-09-29T01:58:47.1562684Z","187.0103923","1.00231069999998","6.640625","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","188","2026-09-29T01:58:48.1603465Z","188.0145517","1.00415940000002","10.15625","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","189","2026-09-29T01:58:49.1504580Z","189.0045898","0.990038099999992","8.92857142857143","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","190","2026-09-29T01:58:50.1478038Z","190.0020249","0.99743509999999","11.328125","0.195814745240068","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","191","2026-09-29T01:58:51.1471695Z","191.0014363","0.999411400000014","4.1015625","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","192","2026-09-29T01:58:52.1488060Z","192.0024062","1.0009699","6.640625","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","193","2026-09-29T01:58:53.1477332Z","193.0018223","0.999416099999991","10.3515625","0.195426609597346","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","194","2026-09-29T01:58:54.1475884Z","194.0016805","0.999858200000006","8.3984375","0.195340199240251","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","195","2026-09-29T01:58:55.1580052Z","195.012162","1.0104815","4.80769230769231","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","196","2026-09-29T01:58:56.1577717Z","196.0119047","0.999742700000013","8.59375","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","197","2026-09-29T01:58:57.1592382Z","197.013394","1.0014893","10.9375","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","198","2026-09-29T01:58:58.1545104Z","198.0086071","0.995213099999972","8.41487279843444","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","199","2026-09-29T01:58:59.1534366Z","199.007513","0.998905900000011","7.99220272904483","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","200","2026-09-29T01:59:00.1503494Z","200.0043516","0.99683859999999","12.1031746031746","0","18792","0.015625","0.195931919169264","1.95703125","12.578125","5","174","0","0" +"2","allow","201","2026-09-29T01:59:01.1562291Z","201.0102653","1.00591370000001","7.5","0.194164270752052","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","202","2026-09-29T01:59:02.1617386Z","202.0157273","1.00546199999999","7.421875","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","203","2026-09-29T01:59:03.1499594Z","203.0040452","0.988317899999998","7.14285714285714","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","204","2026-09-29T01:59:04.1535445Z","204.0070786","1.00303340000002","11.3461538461538","0.194721830798452","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","205","2026-09-29T01:59:05.1540378Z","205.0081762","1.00109759999998","7.03125","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","206","2026-09-29T01:59:06.1591858Z","206.0133327","1.0051565","5.6640625","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","207","2026-09-29T01:59:07.1474451Z","207.0014862","0.98815350000001","5.55555555555556","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","208","2026-09-29T01:59:08.1505829Z","208.0046359","1.00314969999999","11.1328125","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","209","2026-09-29T01:59:09.1511429Z","209.0053082","1.00067230000002","18.75","0.195181279625704","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","210","2026-09-29T01:59:10.1489323Z","210.0030055","0.997697299999999","8.7890625","0.19576328411433","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","211","2026-09-29T01:59:11.1591813Z","211.0132098","1.0102043","8.65384615384615","0.193339604672045","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","212","2026-09-29T01:59:12.1585893Z","212.012773","0.999563199999983","8.59375","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","213","2026-09-29T01:59:13.1574556Z","213.0116879","0.998914900000017","7.2265625","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","214","2026-09-29T01:59:14.1523618Z","214.0065587","0.994870800000001","17.3828125","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","215","2026-09-29T01:59:15.1550756Z","215.0090851","1.00252639999999","11.9140625","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","216","2026-09-29T01:59:16.1586804Z","216.0127701","1.00368499999999","4.8828125","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","217","2026-09-29T01:59:17.1612082Z","217.0148067","1.0020366","8.59375","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","218","2026-09-29T01:59:18.1465915Z","218.0006537","0.985847000000007","9.92063492063492","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","219","2026-09-29T01:59:19.1514244Z","219.0055104","1.0048567","9.49612403100775","0.194368510455271","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","220","2026-09-29T01:59:20.1485598Z","220.0025427","0.997032300000001","7.08661417322835","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","221","2026-09-29T01:59:21.1613986Z","221.0155136","1.0129709","9.03846153846154","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","222","2026-09-29T01:59:22.1469543Z","222.0009815","0.985467900000003","5.15873015873016","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","223","2026-09-29T01:59:23.1508769Z","223.0052656","1.00428410000001","6.0546875","0.194479331097643","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","224","2026-09-29T01:59:24.1606619Z","224.0148298","1.0095642","8.46153846153846","0.193462188932611","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","225","2026-09-29T01:59:25.1581427Z","225.0121668","0.997336999999987","9.375","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","226","2026-09-29T01:59:26.1590418Z","226.0132149","1.00104809999999","5.6640625","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","227","2026-09-29T01:59:27.1622738Z","227.0162373","1.00302240000002","5.2734375","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","228","2026-09-29T01:59:28.1473430Z","228.0014727","0.985235399999993","9.52380952380952","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","229","2026-09-29T01:59:29.1506822Z","229.0047514","1.00327870000001","9.1796875","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","230","2026-09-29T01:59:30.1522289Z","230.0065367","1.00178529999999","11.1538461538462","0.194964430003117","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","231","2026-09-29T01:59:31.1497925Z","231.0038788","0.997342099999997","11.1111111111111","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","232","2026-09-29T01:59:32.1471094Z","232.001157","0.997278199999982","6.25","0.195845552424593","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","233","2026-09-29T01:59:33.1493096Z","233.0033896","1.00223260000001","7.421875","0.194877416679519","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","234","2026-09-29T01:59:34.1601084Z","234.0141869","1.01079730000001","5.76923076923077","0.193226178977722","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","235","2026-09-29T01:59:35.1520925Z","235.0062513","0.992064400000004","7.421875","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","236","2026-09-29T01:59:36.1543723Z","236.0083727","1.00212139999999","16.2109375","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","237","2026-09-29T01:59:37.1570245Z","237.0111144","1.0027417","9.375","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","238","2026-09-29T01:59:38.1557162Z","238.0098401","0.998725699999994","9.9609375","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","239","2026-09-29T01:59:39.1552725Z","239.0093268","0.999486700000006","5.078125","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","240","2026-09-29T01:59:40.1548707Z","240.0089331","0.999606299999982","15.0390625","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","241","2026-09-29T01:59:41.1565590Z","241.0105921","1.00165900000002","12.3046875","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","242","2026-09-29T01:59:42.1581329Z","242.012172","1.0015799","4.8828125","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","243","2026-09-29T01:59:43.1468236Z","243.0009527","0.988780700000007","5.75396825396825","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","244","2026-09-29T01:59:44.1499666Z","244.0039236","1.00297089999998","5.87084148727984","0.19473396486379","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","245","2026-09-29T01:59:45.1498704Z","245.0039031","0.999979500000023","9.55165692007797","0.195316503988327","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","246","2026-09-29T01:59:46.1518026Z","246.005356","1.00145289999998","8.7890625","0.195029142159361","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","247","2026-09-29T01:59:47.1617900Z","247.0155652","1.01020920000002","9.42307692307692","0.193338666882064","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","248","2026-09-29T01:59:48.1611850Z","248.0152647","0.999699499999991","8.3984375","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","249","2026-09-29T01:59:49.1600109Z","249.0141399","0.998875199999986","26.171875","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","250","2026-09-29T01:59:50.1617810Z","250.0159225","1.00178260000001","4.8828125","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","251","2026-09-29T01:59:51.1502839Z","251.0043226","0.988400100000007","5.35714285714286","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","252","2026-09-29T01:59:52.1478580Z","252.0014186","0.997095999999999","5.6640625","0.195881339409646","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","253","2026-09-29T01:59:53.1617186Z","253.0159289","1.01451029999998","8.84615384615385","0.1925189916751","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","254","2026-09-29T01:59:54.1512695Z","254.0052801","0.989351200000016","8.73015873015873","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","255","2026-09-29T01:59:55.1511715Z","255.0052637","0.999983600000007","12.890625","0.195315703177531","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","256","2026-09-29T01:59:56.1495369Z","256.0035646","0.998300900000004","6.640625","0.195644920284054","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","257","2026-09-29T01:59:57.1528453Z","257.0068387","1.0032741","16.3461538461538","0.19467511420857","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","258","2026-09-29T01:59:58.1581632Z","258.0123617","1.00552299999998","4.6875","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","259","2026-09-29T01:59:59.1491057Z","259.0032074","0.990845700000023","8.92857142857143","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","260","2026-09-29T02:00:00.1555694Z","260.0098907","1.00668329999996","9.80769230769231","0.19401583397679","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","261","2026-09-29T02:00:01.1532651Z","261.0073241","0.997433400000034","10.9375","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","262","2026-09-29T02:00:02.1618939Z","262.0161636","1.00883949999997","26.5625","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","263","2026-09-29T02:00:03.1608751Z","263.0150076","0.99884400000002","7.2265625","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","264","2026-09-29T02:00:04.1483858Z","264.0023395","0.987331900000015","7.53968253968254","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","265","2026-09-29T02:00:05.1596240Z","265.0137268","1.01138729999997","7.88461538461539","0.193113459107116","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","266","2026-09-29T02:00:06.1566424Z","266.0106823","0.996955500000013","11.1328125","0","18792","0","0","1.95703125","12.578125","5","174","0","0" +"2","allow","267","2026-09-29T02:00:07.1604689Z","267.0144256","1.0037433","8.3984375","0","18792","0","0","2.01953125","12.75390625","7","174","0","0" +"2","allow","268","2026-09-29T02:00:08.1573482Z","268.0113907","0.996965100000011","4.296875","0","18792","0","0","2.01953125","12.75390625","7","174","0","0" +"2","allow","269","2026-09-29T02:00:09.1584971Z","269.0124786","1.00108790000002","10.15625","0","18792","0","0","2.01953125","12.75390625","7","174","0","0" +"2","allow","270","2026-09-29T02:00:10.1475697Z","270.0011641","0.988685499999974","6.34920634920635","0","18792","0","0","2.01953125","12.75390625","7","174","0","0" +"2","allow","271","2026-09-29T02:00:11.1605953Z","271.0146034","1.01343930000002","8.84615384615385","0","18792","0","0","2.01953125","12.75390625","7","174","0","0" +"2","allow","272","2026-09-29T02:00:12.1571280Z","272.0113368","0.996733399999982","7.03125","0","18792","0","0","2.01953125","12.75390625","7","174","0","0" +"2","allow","273","2026-09-29T02:00:13.1537980Z","273.0079273","0.996590500000025","8.984375","0","18792","0","0","2.01953125","12.75390625","7","174","0","0" +"2","allow","274","2026-09-29T02:00:14.1614215Z","274.0154664","1.00753909999997","5.2734375","0","18792","0","0","2.01953125","12.75390625","7","174","0","0" +"2","allow","275","2026-09-29T02:00:15.1613096Z","275.0155655","1.0000991","5.6640625","0","18792","0","0","2.01953125","12.75390625","7","174","0","0" +"2","allow","276","2026-09-29T02:00:16.1517139Z","276.0056368","0.990071300000011","11.0453648915187","0.197271146027562","18792","0","0","2.01953125","12.75390625","7","174","0","0" +"2","allow","277","2026-09-29T02:00:17.1514595Z","277.0054052","0.999768399999994","10.4125736738703","0","18792","0","0","2.01953125","12.75390625","7","174","0","0" +"2","allow","278","2026-09-29T02:00:18.1500426Z","278.0041543","0.998749099999998","7.421875","0.195557122404416","18792","0","0","2.01953125","12.75390625","7","174","0","0" +"2","allow","279","2026-09-29T02:00:19.1593196Z","279.013396","1.00924170000002","18.4615384615385","0.193524009164501","18792","0","0","2.01953125","12.75390625","7","174","0","0" +"2","allow","280","2026-09-29T02:00:20.1499285Z","280.0040854","0.990689400000008","39.8809523809524","0","18792","0.015625","0.19714806679066","2.01953125","12.75390625","7","174","0","0" +"2","allow","281","2026-09-29T02:00:21.1500154Z","281.0039997","0.9999143","18.75","0.195329239715844","18792","0.015625","0.195329239715844","2.01953125","12.75390625","7","174","0","0" +"2","allow","282","2026-09-29T02:00:22.1500497Z","282.0041088","1.00010909999997","11.71875","0.195291193730769","18792","0","0","2.01953125","12.75390625","7","174","0","0" +"2","allow","283","2026-09-29T02:00:23.1610199Z","283.0151295","1.01102070000002","13.4615384615385","0.1931834827912","18792","0","0","2.01953125","12.75390625","7","174","0","0" +"2","allow","284","2026-09-29T02:00:24.1571718Z","284.0113466","0.996217099999967","11.1328125","0","18792","0","0","2.01953125","12.75390625","7","174","0","0" +"2","allow","285","2026-09-29T02:00:25.1575642Z","285.0117116","1.00036500000004","6.4453125","0","18792","0","0","2.01953125","12.75390625","7","174","0","0" +"2","allow","286","2026-09-29T02:00:26.1553877Z","286.0094574","0.997745799999961","6.0546875","0","18792","0","0","2.01953125","12.75390625","7","174","0","0" +"2","allow","287","2026-09-29T02:00:27.1562660Z","287.0102579","1.00080050000003","5.6640625","0","18792","0","0","2.01953125","12.75390625","7","174","0","0" +"2","allow","288","2026-09-29T02:00:28.1575803Z","288.0117568","1.0014989","5.46875","0","18792","0","0","2.01953125","12.75390625","7","174","0","0" +"2","allow","289","2026-09-29T02:00:29.1546076Z","289.0085441","0.996787299999994","11.71875","0","18792","0","0","2.01953125","12.75390625","7","174","0","0" +"2","allow","290","2026-09-29T02:00:30.1506444Z","290.0047097","0.996165599999983","9.52380952380952","0","18792","0","0","2.01953125","12.75390625","7","174","0","0" +"2","allow","291","2026-09-29T02:00:31.1523980Z","291.0065201","1.00181040000001","11.9230769230769","0.194959545239297","18792","0.015625","0.194959545239297","2.01953125","12.75390625","7","174","0","0" +"2","allow","292","2026-09-29T02:00:32.1571280Z","292.0112396","1.00471950000002","9.375","0","18792","0","0","2.01953125","12.75390625","7","174","0","0" +"2","allow","293","2026-09-29T02:00:33.1533962Z","293.0070483","0.995808699999998","9.375","0","18792","0","0","2.01953125","12.75390625","7","174","0","0" +"2","allow","294","2026-09-29T02:00:34.1562065Z","294.0103124","1.00326409999997","5.2734375","0","18792","0","0","2.01953125","12.75390625","7","174","0","0" +"2","allow","295","2026-09-29T02:00:35.1598795Z","295.0139637","1.0036513","4.1015625","0","18792","0","0","2.01953125","12.75390625","7","174","0","0" +"2","allow","296","2026-09-29T02:00:36.1604439Z","296.0146476","1.00068390000001","6.25","0","18792","0","0","2.01953125","12.75390625","7","174","0","0" +"2","allow","297","2026-09-29T02:00:37.1590406Z","297.0131277","0.998480099999995","8.7890625","0","18792","0","0","2.01953125","12.75390625","7","174","0","0" +"2","allow","298","2026-09-29T02:00:38.1569606Z","298.01108","0.997952300000009","5.2734375","0","18792","0","0","2.01953125","12.75390625","7","174","0","0" +"2","allow","299","2026-09-29T02:00:39.1568502Z","299.0109047","0.999824699999976","8.59375","0","18792","0","0","2.01953125","12.75390625","7","174","0","0" +"2","allow","300","2026-09-29T02:00:40.1504619Z","300.0045551","0.993650400000035","6.94444444444444","0","18792","0","0","2.01953125","12.75390625","7","174","0","0" diff --git a/docs/benchmarks/2026-09-29-v0.2.0/results/run-2-baseline.csv b/docs/benchmarks/2026-09-29-v0.2.0/results/run-2-baseline.csv new file mode 100644 index 0000000..991f8d9 --- /dev/null +++ b/docs/benchmarks/2026-09-29-v0.2.0/results/run-2-baseline.csv @@ -0,0 +1,301 @@ +"run","scenario","sample","utc","elapsed_s","interval_s","system_cpu_pct","collector_cpu_pct","pid","process_cpu_s","cpu_pct","private_mib","working_set_mib","threads","handles","io_read_bytes_s","io_write_bytes_s" +"2","baseline","1","2026-09-29T02:06:44.3847291Z","1.0066061","1.0066034","11.9373776908023","0","","","","","","","","","" +"2","baseline","2","2026-09-29T02:06:45.3832162Z","2.0050419","0.9984358","9.16179337231969","0","","","","","","","","","" +"2","baseline","3","2026-09-29T02:06:46.3842280Z","3.0060679","1.001026","10.7421875","0","","","","","","","","","" +"2","baseline","4","2026-09-29T02:06:47.3961405Z","4.0179818","1.0119139","9.42307692307692","0","","","","","","","","","" +"2","baseline","5","2026-09-29T02:06:48.3806658Z","5.0025749","0.984593100000001","5.55555555555556","0","","","","","","","","","" +"2","baseline","6","2026-09-29T02:06:49.3787396Z","6.0005953","0.9980204","6.27450980392157","0","","","","","","","","","" +"2","baseline","7","2026-09-29T02:06:50.3811691Z","7.0030368","1.0024415","7.00389105058366","0","","","","","","","","","" +"2","baseline","8","2026-09-29T02:06:51.3917341Z","8.0136237","1.0105869","10.1923076923077","0","","","","","","","","","" +"2","baseline","9","2026-09-29T02:06:52.3908220Z","9.0127049","0.999081199999999","6.640625","0","","","","","","","","","" +"2","baseline","10","2026-09-29T02:06:53.3855744Z","10.0074693","0.994764400000001","8.73015873015873","0","","","","","","","","","" +"2","baseline","11","2026-09-29T02:06:54.3856377Z","11.0075831","1.0001138","11.1328125","0.195290275966595","","","","","","","","","" +"2","baseline","12","2026-09-29T02:06:55.3871123Z","12.0090646","1.0014815","10.1923076923077","0.195023572577227","","","","","","","","","" +"2","baseline","13","2026-09-29T02:06:56.3878102Z","13.009695","1.0006304","10.7421875","0","","","","","","","","","" +"2","baseline","14","2026-09-29T02:06:57.3866156Z","14.0085137","0.998818700000001","12.133072407045","0","","","","","","","","","" +"2","baseline","15","2026-09-29T02:06:58.3858734Z","15.0077396","0.999225899999999","11.3725490196078","0.195463808534187","","","","","","","","","" +"2","baseline","16","2026-09-29T02:06:59.3872637Z","16.0086146","1.000875","14.5631067961165","0","","","","","","","","","" +"2","baseline","17","2026-09-29T02:07:00.3888166Z","17.0107604","1.0021458","6.4453125","0","","","","","","","","","" +"2","baseline","18","2026-09-29T02:07:01.3923094Z","18.0142713","1.0035109","6.0546875","0","","","","","","","","","" +"2","baseline","19","2026-09-29T02:07:02.3960746Z","19.0179662","1.0036949","5.2734375","0","","","","","","","","","" +"2","baseline","20","2026-09-29T02:07:03.3811732Z","20.0029755","0.985009299999998","6.54761904761905","0","","","","","","","","","" +"2","baseline","21","2026-09-29T02:07:04.3820439Z","21.0039887","1.0010132","11.328125","0","","","","","","","","","" +"2","baseline","22","2026-09-29T02:07:05.3852001Z","22.0072394","1.0032507","6.4453125","0","","","","","","","","","" +"2","baseline","23","2026-09-29T02:07:06.3898866Z","23.0118719","1.0046325","9.61538461538461","0","","","","","","","","","" +"2","baseline","24","2026-09-29T02:07:07.3940192Z","24.0159644","1.0040925","7.421875","0","","","","","","","","","" +"2","baseline","25","2026-09-29T02:07:08.3919512Z","25.0138521","0.9978877","5.46875","0","","","","","","","","","" +"2","baseline","26","2026-09-29T02:07:09.3944501Z","26.0163398","1.0024877","6.0546875","0","","","","","","","","","" +"2","baseline","27","2026-09-29T02:07:10.3786577Z","27.0006084","0.9842686","6.34920634920635","0","","","","","","","","","" +"2","baseline","28","2026-09-29T02:07:11.3813479Z","28.0031205","1.0025121","7.6171875","0","","","","","","","","","" +"2","baseline","29","2026-09-29T02:07:12.3818598Z","29.0037736","1.0006531","7.03125","0","","","","","","","","","" +"2","baseline","30","2026-09-29T02:07:13.3944181Z","30.016387","1.0126134","6.73076923076923","0","","","","","","","","","" +"2","baseline","31","2026-09-29T02:07:14.3924591Z","31.0145347","0.998147700000001","10.3515625","0","","","","","","","","","" +"2","baseline","32","2026-09-29T02:07:15.3933555Z","32.0152135","1.0006788","5.859375","0","","","","","","","","","" +"2","baseline","33","2026-09-29T02:07:16.3893954Z","33.0113871","0.996173599999999","4.296875","0","","","","","","","","","" +"2","baseline","34","2026-09-29T02:07:17.3868765Z","34.009105","0.997717899999998","5.46875","0","","","","","","","","","" +"2","baseline","35","2026-09-29T02:07:18.3958600Z","35.0177325","1.0086275","7.6171875","0","","","","","","","","","" +"2","baseline","36","2026-09-29T02:07:19.3791157Z","36.0004594","0.982726899999996","6.16302186878728","0","","","","","","","","","" +"2","baseline","37","2026-09-29T02:07:20.3781754Z","37.0000506","0.999591200000005","5.65302144249513","0","","","","","","","","","" +"2","baseline","38","2026-09-29T02:07:21.3887631Z","38.0106598","1.0106092","5.57692307692308","0","","","","","","","","","" +"2","baseline","39","2026-09-29T02:07:22.3864346Z","39.0083826","0.997722799999998","10.546875","0","","","","","","","","","" +"2","baseline","40","2026-09-29T02:07:23.3865817Z","40.0084147","1.0000321","9.1796875","0","","","","","","","","","" +"2","baseline","41","2026-09-29T02:07:24.3883905Z","41.0102019","1.0017872","12.5","0","","","","","","","","","" +"2","baseline","42","2026-09-29T02:07:25.3900282Z","42.0119104","1.0017085","6.45792563600783","0","","","","","","","","","" +"2","baseline","43","2026-09-29T02:07:26.3904609Z","43.0123005","1.0003901","4.87329434697856","0","","","","","","","","","" +"2","baseline","44","2026-09-29T02:07:27.3938823Z","44.0156793","1.0033788","8.3984375","0","","","","","","","","","" +"2","baseline","45","2026-09-29T02:07:28.3835468Z","45.0054674","0.989788100000005","5.95238095238095","0","","","","","","","","","" +"2","baseline","46","2026-09-29T02:07:29.3859823Z","46.0078719","1.0024045","10.8108108108108","0.194843997607753","","","","","","","","","" +"2","baseline","47","2026-09-29T02:07:30.3919641Z","47.0138479","1.005976","8.75486381322957","0","","","","","","","","","" +"2","baseline","48","2026-09-29T02:07:31.3942765Z","48.0162432","1.0023953","6.0546875","0","","","","","","","","","" +"2","baseline","49","2026-09-29T02:07:32.3789107Z","49.0009128","0.984669599999997","10.7142857142857","0","","","","","","","","","" +"2","baseline","50","2026-09-29T02:07:33.3836349Z","50.00552","1.0046072","9.765625","0","","","","","","","","","" +"2","baseline","51","2026-09-29T02:07:34.3854645Z","51.0074279","1.0019079","11.9140625","0","","","","","","","","","" +"2","baseline","52","2026-09-29T02:07:35.3862428Z","52.0081079","1.00068","14.6153846153846","0.195179777751129","","","","","","","","","" +"2","baseline","53","2026-09-29T02:07:36.3849849Z","53.0068448","0.998736899999997","18.8492063492063","0","","","","","","","","","" +"2","baseline","54","2026-09-29T02:07:37.3912925Z","54.0131553","1.0063105","9.42307692307692","0","","","","","","","","","" +"2","baseline","55","2026-09-29T02:07:38.4106150Z","55.0325571","1.0194018","5.38461538461539","0.191595208091648","","","","","","","","","" +"2","baseline","56","2026-09-29T02:07:39.3917775Z","56.0138514","0.981294300000002","9.72222222222222","0","","","","","","","","","" +"2","baseline","57","2026-09-29T02:07:40.3952371Z","57.0171498","1.0032984","8.0078125","0","","","","","","","","","" +"2","baseline","58","2026-09-29T02:07:41.3931497Z","58.0151823","0.998032500000001","4.6875","0","","","","","","","","","" +"2","baseline","59","2026-09-29T02:07:42.3953131Z","59.0168493","1.001667","7.2265625","0","","","","","","","","","" +"2","baseline","60","2026-09-29T02:07:43.3832744Z","60.0052554","0.988406099999999","7.93650793650794","0","","","","","","","","","" +"2","baseline","61","2026-09-29T02:07:44.3872830Z","61.0092561","1.0040007","15.1923076923077","0","","","","","","","","","" +"2","baseline","62","2026-09-29T02:07:45.3901584Z","62.01191","1.00265390000001","7.8125","0","","","","","","","","","" +"2","baseline","63","2026-09-29T02:07:46.3872144Z","63.0090392","0.997129199999996","5.078125","0","","","","","","","","","" +"2","baseline","64","2026-09-29T02:07:47.3826308Z","64.0046276","0.995588399999995","15.0793650793651","0","","","","","","","","","" +"2","baseline","65","2026-09-29T02:07:48.3872841Z","65.0092504","1.00462280000001","7.30769230769231","0","","","","","","","","","" +"2","baseline","66","2026-09-29T02:07:49.3909426Z","66.0128191","1.0035687","5.46875","0","","","","","","","","","" +"2","baseline","67","2026-09-29T02:07:50.3928738Z","67.014825","1.0020059","4.296875","0","","","","","","","","","" +"2","baseline","68","2026-09-29T02:07:51.3944716Z","68.0164986","1.00167359999999","7.2265625","0","","","","","","","","","" +"2","baseline","69","2026-09-29T02:07:52.3839693Z","69.0057798","0.989281200000008","7.14285714285714","0","","","","","","","","","" +"2","baseline","70","2026-09-29T02:07:53.3951262Z","70.0170012","1.0112214","7.88461538461539","0","","","","","","","","","" +"2","baseline","71","2026-09-29T02:07:54.3813794Z","71.0033725","0.986371300000002","10.3174603174603","0","","","","","","","","","" +"2","baseline","72","2026-09-29T02:07:55.3873474Z","72.0093085","1.00593600000001","9.03846153846154","0","","","","","","","","","" +"2","baseline","73","2026-09-29T02:07:56.3853062Z","73.0071565","0.997847999999991","12.8968253968254","0","","","","","","","","","" +"2","baseline","74","2026-09-29T02:07:57.3822443Z","74.004095","0.996938499999999","9.375","0","","","","","","","","","" +"2","baseline","75","2026-09-29T02:07:58.3830745Z","75.0050726","1.0009776","5.6640625","0","","","","","","","","","" +"2","baseline","76","2026-09-29T02:07:59.3823693Z","76.0042934","0.999220800000003","5.46875","0","","","","","","","","","" +"2","baseline","77","2026-09-29T02:08:00.3832149Z","77.0050193","1.00072590000001","6.640625","0","","","","","","","","","" +"2","baseline","78","2026-09-29T02:08:01.3876583Z","78.0095087","1.0044894","7.69230769230769","0","","","","","","","","","" +"2","baseline","79","2026-09-29T02:08:02.3934608Z","79.0152379","1.0057292","6.25","0","","","","","","","","","" +"2","baseline","80","2026-09-29T02:08:03.3930003Z","80.0148399","0.999601999999996","8.0078125","0","","","","","","","","","" +"2","baseline","81","2026-09-29T02:08:04.3891595Z","81.0110675","0.996227599999997","9.5703125","0","","","","","","","","","" +"2","baseline","82","2026-09-29T02:08:05.3858122Z","82.007702","0.996634499999999","6.73267326732673","0","","","","","","","","","" +"2","baseline","83","2026-09-29T02:08:06.3890951Z","83.0109875","1.0032855","9.2485549132948","0","","","","","","","","","" +"2","baseline","84","2026-09-29T02:08:07.3901133Z","84.0118858","1.0008983","7.8125","0","","","","","","","","","" +"2","baseline","85","2026-09-29T02:08:08.3883966Z","85.0102726","0.998386799999992","6.640625","0","","","","","","","","","" +"2","baseline","86","2026-09-29T02:08:09.3840227Z","86.0060296","0.995756999999998","10.7142857142857","0","","","","","","","","","" +"2","baseline","87","2026-09-29T02:08:10.3848110Z","87.0067313","1.00070170000001","6.4453125","0","","","","","","","","","" +"2","baseline","88","2026-09-29T02:08:11.3849310Z","88.0069363","1.00020499999999","10.9375","0","","","","","","","","","" +"2","baseline","89","2026-09-29T02:08:12.3872105Z","89.0091073","1.002171","7.88461538461539","0","","","","","","","","","" +"2","baseline","90","2026-09-29T02:08:13.3903373Z","90.0123466","1.0032393","6.8359375","0","","","","","","","","","" +"2","baseline","91","2026-09-29T02:08:14.3892597Z","91.0111305","0.998783899999992","12.3046875","0","","","","","","","","","" +"2","baseline","92","2026-09-29T02:08:15.3939976Z","92.0158729","1.0047424","7.2265625","0","","","","","","","","","" +"2","baseline","93","2026-09-29T02:08:16.3827536Z","93.0047805","0.988907600000005","7.53968253968254","0","","","","","","","","","" +"2","baseline","94","2026-09-29T02:08:17.3860924Z","94.0075297","1.0027492","5.6640625","0","","","","","","","","","" +"2","baseline","95","2026-09-29T02:08:18.3882314Z","95.010093","1.00256330000001","9.63391136801541","0.194813135489798","","","","","","","","","" +"2","baseline","96","2026-09-29T02:08:19.3859945Z","96.0079914","0.997898399999997","14.3984220907298","0.19572383320787","","","","","","","","","" +"2","baseline","97","2026-09-29T02:08:20.3861353Z","97.0079897","0.999998300000001","12.1621621621622","0","","","","","","","","","" +"2","baseline","98","2026-09-29T02:08:21.3881641Z","98.0099805","1.0019908","8.203125","0","","","","","","","","","" +"2","baseline","99","2026-09-29T02:08:22.3878921Z","99.0098709","0.999890399999998","5.6640625","0","","","","","","","","","" +"2","baseline","100","2026-09-29T02:08:23.3820550Z","100.0039779","0.994107","10.515873015873","0","","","","","","","","","" +"2","baseline","101","2026-09-29T02:08:24.3841731Z","101.0061691","1.0021912","8.203125","0","","","","","","","","","" +"2","baseline","102","2026-09-29T02:08:25.3847351Z","102.0068271","1.000658","11.9140625","0","","","","","","","","","" +"2","baseline","103","2026-09-29T02:08:26.3855195Z","103.0073223","1.0004952","7.421875","0","","","","","","","","","" +"2","baseline","104","2026-09-29T02:08:27.3857015Z","104.0078529","1.00053059999999","14.2023346303502","0.390417844291822","","","","","","","","","" +"2","baseline","105","2026-09-29T02:08:28.3896862Z","105.0117238","1.00387090000001","8.49420849420849","0","","","","","","","","","" +"2","baseline","106","2026-09-29T02:08:29.3930343Z","106.0144749","1.0027511","5.2734375","0","","","","","","","","","" +"2","baseline","107","2026-09-29T02:08:30.3886827Z","107.0100725","0.995597599999996","5.2734375","0","","","","","","","","","" +"2","baseline","108","2026-09-29T02:08:31.3879227Z","108.0097323","0.999659800000003","39.0625","0","","","","","","","","","" +"2","baseline","109","2026-09-29T02:08:32.3956028Z","109.0174711","1.0077388","24.0234375","0","","","","","","","","","" +"2","baseline","110","2026-09-29T02:08:33.3793844Z","110.0013439","0.9838728","14.0873015873016","0","","","","","","","","","" +"2","baseline","111","2026-09-29T02:08:34.3922014Z","111.0141649","1.012821","6.92307692307692","0","","","","","","","","","" +"2","baseline","112","2026-09-29T02:08:35.3906837Z","112.012714","0.998549099999991","9.765625","0","","","","","","","","","" +"2","baseline","113","2026-09-29T02:08:36.3907894Z","113.012714","1","10.3515625","0","","","","","","","","","" +"2","baseline","114","2026-09-29T02:08:37.3880756Z","114.0098729","0.997158900000002","6.0546875","0","","","","","","","","","" +"2","baseline","115","2026-09-29T02:08:38.3841465Z","115.0060014","0.996128499999998","8.73015873015873","0","","","","","","","","","" +"2","baseline","116","2026-09-29T02:08:39.3893128Z","116.0111994","1.00519800000001","13.6538461538462","0","","","","","","","","","" +"2","baseline","117","2026-09-29T02:08:40.3875952Z","117.0094702","0.9982708","17.96875","0","","","","","","","","","" +"2","baseline","118","2026-09-29T02:08:41.3844799Z","118.0064116","0.996941399999997","10.9126984126984","0","","","","","","","","","" +"2","baseline","119","2026-09-29T02:08:42.3939161Z","119.0157906","1.009379","9.80769230769231","0","","","","","","","","","" +"2","baseline","120","2026-09-29T02:08:43.3785030Z","120.0004547","0.984664100000003","4.96031746031746","0","","","","","","","","","" +"2","baseline","121","2026-09-29T02:08:44.3775842Z","120.999547","0.999092300000001","7.03125","0","","","","","","","","","" +"2","baseline","122","2026-09-29T02:08:45.3796194Z","122.0015859","1.0020389","8.984375","0","","","","","","","","","" +"2","baseline","123","2026-09-29T02:08:46.3884400Z","123.0103203","1.00873439999999","9.03846153846154","0","","","","","","","","","" +"2","baseline","124","2026-09-29T02:08:47.3861268Z","124.0079469","0.997626600000004","14.453125","0.195777157505623","","","","","","","","","" +"2","baseline","125","2026-09-29T02:08:48.3872214Z","125.0091441","1.00119720000001","16.015625","0","","","","","","","","","" +"2","baseline","126","2026-09-29T02:08:49.3849606Z","126.0068468","0.997702699999991","9.92063492063492","0","","","","","","","","","" +"2","baseline","127","2026-09-29T02:08:50.3840060Z","127.0059718","0.999125000000006","13.0859375","0","","","","","","","","","" +"2","baseline","128","2026-09-29T02:08:51.3832602Z","128.0051008","0.999129000000011","6.4453125","0","","","","","","","","","" +"2","baseline","129","2026-09-29T02:08:52.3825872Z","129.0044271","0.999326299999979","7.2265625","0","","","","","","","","","" +"2","baseline","130","2026-09-29T02:08:53.3874503Z","130.0092539","1.00482680000002","9.03846153846154","0","","","","","","","","","" +"2","baseline","131","2026-09-29T02:08:54.3891073Z","131.0109578","1.0017039","6.8359375","0","","","","","","","","","" +"2","baseline","132","2026-09-29T02:08:55.3897220Z","132.0117664","1.0008086","8.984375","0","","","","","","","","","" +"2","baseline","133","2026-09-29T02:08:56.3896912Z","133.011556","0.999789599999986","7.03125","0","","","","","","","","","" +"2","baseline","134","2026-09-29T02:08:57.3920772Z","134.0138889","1.0023329","7.82778864970646","0","","","","","","","","","" +"2","baseline","135","2026-09-29T02:08:58.3960167Z","135.0178703","1.00398140000001","4.48343079922027","0","","","","","","","","","" +"2","baseline","136","2026-09-29T02:08:59.3801060Z","136.0019995","0.984129199999984","7.14285714285714","0","","","","","","","","","" +"2","baseline","137","2026-09-29T02:09:00.3920020Z","137.0139827","1.0119832","6.92307692307692","0","","","","","","","","","" +"2","baseline","138","2026-09-29T02:09:01.3929191Z","138.0147883","1.00080560000001","5.2734375","0","","","","","","","","","" +"2","baseline","139","2026-09-29T02:09:02.3926666Z","139.0146128","0.999824499999988","7.03125","0","","","","","","","","","" +"2","baseline","140","2026-09-29T02:09:03.3962852Z","140.0182948","1.00368200000003","6.8359375","0","","","","","","","","","" +"2","baseline","141","2026-09-29T02:09:04.3830238Z","141.0049262","0.986631399999993","6.54761904761905","0","","","","","","","","","" +"2","baseline","142","2026-09-29T02:09:05.3917867Z","142.0136564","1.0087302","9.61538461538461","0","","","","","","","","","" +"2","baseline","143","2026-09-29T02:09:06.3873702Z","143.0094859","0.995829499999985","24.21875","0","","","","","","","","","" +"2","baseline","144","2026-09-29T02:09:07.3875522Z","144.0095657","1.00007980000001","7.8125","0","","","","","","","","","" +"2","baseline","145","2026-09-29T02:09:08.3859237Z","145.007927","0.998361299999999","11.3725490196078","0.195633083934644","","","","","","","","","" +"2","baseline","146","2026-09-29T02:09:09.3877527Z","146.009759","1.00183200000001","10.1167315175097","0","","","","","","","","","" +"2","baseline","147","2026-09-29T02:09:10.3900489Z","147.0119344","1.0021754","5.6640625","0","","","","","","","","","" +"2","baseline","148","2026-09-29T02:09:11.3859589Z","148.007874","0.995939599999986","12.992125984252","0.196108780090683","","","","","","","","","" +"2","baseline","149","2026-09-29T02:09:12.3854715Z","149.0072871","0.999413099999998","12.007874015748","0","","","","","","","","","" +"2","baseline","150","2026-09-29T02:09:13.3818499Z","150.0037464","0.996459299999998","11.9140625","0","","","","","","","","","" +"2","baseline","151","2026-09-29T02:09:14.3950222Z","151.0168833","1.01313690000001","5.19230769230769","0","","","","","","","","","" +"2","baseline","152","2026-09-29T02:09:15.3900678Z","152.0119551","0.995071800000005","16.40625","0","","","","","","","","","" +"2","baseline","153","2026-09-29T02:09:16.3893353Z","153.0112128","0.999257699999987","20.3125","0","","","","","","","","","" +"2","baseline","154","2026-09-29T02:09:17.3911852Z","154.0130587","1.00184590000001","4.4921875","0","","","","","","","","","" +"2","baseline","155","2026-09-29T02:09:18.3894118Z","155.0113896","0.998330900000013","4.296875","0","","","","","","","","","" +"2","baseline","156","2026-09-29T02:09:19.3959050Z","156.0177431","1.00635349999999","6.640625","0","","","","","","","","","" +"2","baseline","157","2026-09-29T02:09:20.3903262Z","157.0121667","0.994423600000005","10.3515625","0","","","","","","","","","" +"2","baseline","158","2026-09-29T02:09:21.3886117Z","158.0104253","0.998258599999986","10.15625","0","","","","","","","","","" +"2","baseline","159","2026-09-29T02:09:22.3885115Z","159.0103395","0.999914200000006","5.2734375","0","","","","","","","","","" +"2","baseline","160","2026-09-29T02:09:23.3881766Z","160.0101252","0.999785700000018","7.6171875","0","","","","","","","","","" +"2","baseline","161","2026-09-29T02:09:24.3862594Z","161.0080848","0.997959600000002","6.0546875","0","","","","","","","","","" +"2","baseline","162","2026-09-29T02:09:25.3849877Z","162.0068347","0.998749899999979","11.9047619047619","0","","","","","","","","","" +"2","baseline","163","2026-09-29T02:09:26.3833008Z","163.0054175","0.998582800000008","5.859375","0","","","","","","","","","" +"2","baseline","164","2026-09-29T02:09:27.3859611Z","164.007861","1.0024435","8.57699805068226","0","","","","","","","","","" +"2","baseline","165","2026-09-29T02:09:28.3832941Z","165.005186","0.997324999999989","7.43639921722114","0.195836362269072","","","","","","","","","" +"2","baseline","166","2026-09-29T02:09:29.3857186Z","166.0076187","1.00243270000001","7.8125","0","","","","","","","","","" +"2","baseline","167","2026-09-29T02:09:30.3848997Z","167.0068154","0.999196699999999","6.8359375","0.195469520665951","","","","","","","","","" +"2","baseline","168","2026-09-29T02:09:31.3856032Z","168.0075571","1.00074169999999","12.109375","0","","","","","","","","","" +"2","baseline","169","2026-09-29T02:09:32.3892300Z","169.0111281","1.00357099999999","9.03846153846154","0.194617520833106","","","","","","","","","" +"2","baseline","170","2026-09-29T02:09:33.3938766Z","170.0154296","1.00430150000003","5.46875","0","","","","","","","","","" +"2","baseline","171","2026-09-29T02:09:34.3936826Z","171.0155661","1.0001365","6.25","0","","","","","","","","","" +"2","baseline","172","2026-09-29T02:09:35.3927978Z","172.0147864","0.99922029999999","12.6953125","0","","","","","","","","","" +"2","baseline","173","2026-09-29T02:09:36.3931475Z","173.015038","1.00025160000001","5.6640625","0","","","","","","","","","" +"2","baseline","174","2026-09-29T02:09:37.3946771Z","174.016421","1.00138299999998","6.25","0","","","","","","","","","" +"2","baseline","175","2026-09-29T02:09:38.3789981Z","175.0008515","0.984430500000002","7.34126984126984","0","","","","","","","","","" +"2","baseline","176","2026-09-29T02:09:39.3802510Z","176.0021491","1.00129760000002","8.203125","0","","","","","","","","","" +"2","baseline","177","2026-09-29T02:09:40.3788709Z","177.0008207","0.998671599999994","5.2734375","0","","","","","","","","","" +"2","baseline","178","2026-09-29T02:09:41.3788036Z","178.0006553","0.999834600000014","4.6875","0","","","","","","","","","" +"2","baseline","179","2026-09-29T02:09:42.3910982Z","179.0132557","1.0126004","5.38461538461539","0","","","","","","","","","" +"2","baseline","180","2026-09-29T02:09:43.3899646Z","180.0119487","0.998693000000003","7.6171875","0","","","","","","","","","" +"2","baseline","181","2026-09-29T02:09:44.3881355Z","181.0100429","0.998094199999997","8.3984375","0","","","","","","","","","" +"2","baseline","182","2026-09-29T02:09:45.3870015Z","182.0088924","0.998849499999977","9.765625","0","","","","","","","","","" +"2","baseline","183","2026-09-29T02:09:46.3885912Z","183.0106613","1.0017689","8.7890625","0","","","","","","","","","" +"2","baseline","184","2026-09-29T02:09:47.3890220Z","184.0104163","0.999755000000022","6.8359375","0","","","","","","","","","" +"2","baseline","185","2026-09-29T02:09:48.3911588Z","185.0130552","1.00263889999999","9.5703125","0","","","","","","","","","" +"2","baseline","186","2026-09-29T02:09:49.3814256Z","186.0034783","0.990423099999987","6.34920634920635","0","","","","","","","","","" +"2","baseline","187","2026-09-29T02:09:50.3842460Z","187.0060759","1.0025976","8.203125","0","","","","","","","","","" +"2","baseline","188","2026-09-29T02:09:51.3857805Z","188.0076819","1.00160600000001","11.8677042801556","0","","","","","","","","","" +"2","baseline","189","2026-09-29T02:09:52.3967338Z","189.0185974","1.01091550000001","5.5984555984556","0","","","","","","","","","" +"2","baseline","190","2026-09-29T02:09:53.3938277Z","190.0157371","0.997139699999991","5.859375","0","","","","","","","","","" +"2","baseline","191","2026-09-29T02:09:54.3828313Z","191.0048394","0.989102299999985","7.53968253968254","0","","","","","","","","","" +"2","baseline","192","2026-09-29T02:09:55.3871670Z","192.0093018","1.00446240000002","9.42307692307692","0","","","","","","","","","" +"2","baseline","193","2026-09-29T02:09:56.3852502Z","193.0070949","0.997793099999996","14.8809523809524","0","","","","","","","","","" +"2","baseline","194","2026-09-29T02:09:57.3847244Z","194.0066637","0.999568799999992","14.6484375","0","","","","","","","","","" +"2","baseline","195","2026-09-29T02:09:58.3904032Z","195.0124815","1.00581779999999","11.5384615384615","0","","","","","","","","","" +"2","baseline","196","2026-09-29T02:09:59.3876552Z","196.009616","0.997134500000016","6.0546875","0","","","","","","","","","" +"2","baseline","197","2026-09-29T02:10:00.3903936Z","197.0122627","1.00264669999999","4.296875","0","","","","","","","","","" +"2","baseline","198","2026-09-29T02:10:01.3952742Z","198.01715","1.00488730000001","5.859375","0","","","","","","","","","" +"2","baseline","199","2026-09-29T02:10:02.3820128Z","199.0039881","0.9868381","4.36507936507936","0","","","","","","","","","" +"2","baseline","200","2026-09-29T02:10:03.3843318Z","200.0063397","1.0023516","7.6171875","0","","","","","","","","","" +"2","baseline","201","2026-09-29T02:10:04.3853506Z","201.0073792","1.00103950000002","9.1796875","0","","","","","","","","","" +"2","baseline","202","2026-09-29T02:10:05.3888842Z","202.010808","1.00342879999999","7.11538461538461","0","","","","","","","","","" +"2","baseline","203","2026-09-29T02:10:06.3897182Z","203.0116555","1.00084749999999","10.15625","0","","","","","","","","","" +"2","baseline","204","2026-09-29T02:10:07.3889926Z","204.0110422","0.999386700000002","5.859375","0","","","","","","","","","" +"2","baseline","205","2026-09-29T02:10:08.3866354Z","205.0084724","0.997430199999997","5.859375","0","","","","","","","","","" +"2","baseline","206","2026-09-29T02:10:09.3846237Z","206.0064388","0.997966399999996","14.0873015873016","0","","","","","","","","","" +"2","baseline","207","2026-09-29T02:10:10.3847485Z","207.0066808","1.00024200000001","6.0546875","0","","","","","","","","","" +"2","baseline","208","2026-09-29T02:10:11.3961717Z","208.0179641","1.0112833","10","0","","","","","","","","","" +"2","baseline","209","2026-09-29T02:10:12.3885153Z","209.0103371","0.992372999999986","5.46875","0","","","","","","","","","" +"2","baseline","210","2026-09-29T02:10:13.3901843Z","210.0122288","1.00189170000002","5.859375","0","","","","","","","","","" +"2","baseline","211","2026-09-29T02:10:14.3923831Z","211.0144218","1.00219299999998","6.8359375","0","","","","","","","","","" +"2","baseline","212","2026-09-29T02:10:15.3939208Z","212.0158786","1.0014568","8.3984375","0","","","","","","","","","" +"2","baseline","213","2026-09-29T02:10:16.3907991Z","213.0122747","0.996396099999998","12.5","0","","","","","","","","","" +"2","baseline","214","2026-09-29T02:10:17.3810979Z","214.0030122","0.990737500000023","41.6666666666667","0","","","","","","","","","" +"2","baseline","215","2026-09-29T02:10:18.3934502Z","215.0153311","1.0123189","19.2307692307692","0","","","","","","","","","" +"2","baseline","216","2026-09-29T02:10:19.3867926Z","216.0092576","0.993926499999986","17.7734375","0","","","","","","","","","" +"2","baseline","217","2026-09-29T02:10:20.3815276Z","217.003356","0.994098400000013","10.7142857142857","0","","","","","","","","","" +"2","baseline","218","2026-09-29T02:10:21.3833447Z","218.0051998","1.00184379999999","5.46875","0","","","","","","","","","" +"2","baseline","219","2026-09-29T02:10:22.3949168Z","219.0167856","1.01158580000001","5.76923076923077","0","","","","","","","","","" +"2","baseline","220","2026-09-29T02:10:23.3785457Z","220.0003793","0.9835937","7.73809523809524","0","","","","","","","","","" +"2","baseline","221","2026-09-29T02:10:24.3798581Z","221.0017785","1.00139920000001","6.4453125","0","","","","","","","","","" +"2","baseline","222","2026-09-29T02:10:25.3830997Z","222.0050517","1.0032732","7.2265625","0","","","","","","","","","" +"2","baseline","223","2026-09-29T02:10:26.3959439Z","223.0177883","1.01273659999998","17.1153846153846","0","","","","","","","","","" +"2","baseline","224","2026-09-29T02:10:27.3787141Z","224.0005411","0.982752800000014","10.7142857142857","0","","","","","","","","","" +"2","baseline","225","2026-09-29T02:10:28.3778685Z","224.9997457","0.999204599999985","8.984375","0","","","","","","","","","" +"2","baseline","226","2026-09-29T02:10:29.3823141Z","226.0044681","1.00472240000002","8.203125","0","","","","","","","","","" +"2","baseline","227","2026-09-29T02:10:30.3893843Z","227.0112642","1.0067961","9.03846153846154","0","","","","","","","","","" +"2","baseline","228","2026-09-29T02:10:31.3911622Z","228.0131553","1.00189109999999","7.6171875","0","","","","","","","","","" +"2","baseline","229","2026-09-29T02:10:32.3919352Z","229.0138263","1.00067099999998","5.46875","0","","","","","","","","","" +"2","baseline","230","2026-09-29T02:10:33.3962707Z","230.0188927","1.0050664","4.6875","0","","","","","","","","","" +"2","baseline","231","2026-09-29T02:10:34.3927155Z","231.0145029","0.995610200000016","6.640625","0","","","","","","","","","" +"2","baseline","232","2026-09-29T02:10:35.3921182Z","232.0140043","0.999501399999986","6.8359375","0","","","","","","","","","" +"2","baseline","233","2026-09-29T02:10:36.3809132Z","233.0027315","0.9887272","9.9601593625498","0","","","","","","","","","" +"2","baseline","234","2026-09-29T02:10:37.3907268Z","234.0125893","1.00985780000002","10.727969348659","0","","","","","","","","","" +"2","baseline","235","2026-09-29T02:10:38.3904245Z","235.0122325","0.99964319999998","6.0546875","0","","","","","","","","","" +"2","baseline","236","2026-09-29T02:10:39.3819250Z","236.0039042","0.991671700000012","5.95238095238095","0","","","","","","","","","" +"2","baseline","237","2026-09-29T02:10:40.3846192Z","237.0064199","1.0025157","9.765625","0","","","","","","","","","" +"2","baseline","238","2026-09-29T02:10:41.3842221Z","238.006154","0.999734099999984","6.25","0","","","","","","","","","" +"2","baseline","239","2026-09-29T02:10:42.3809029Z","239.0027642","0.996610200000021","7.6171875","0","","","","","","","","","" +"2","baseline","240","2026-09-29T02:10:43.3832917Z","240.0051185","1.00235429999998","7.6171875","0","","","","","","","","","" +"2","baseline","241","2026-09-29T02:10:44.3823009Z","241.004125","0.999006500000007","6.0546875","0","","","","","","","","","" +"2","baseline","242","2026-09-29T02:10:45.3821688Z","242.0040068","0.999881799999997","5.2734375","0","","","","","","","","","" +"2","baseline","243","2026-09-29T02:10:46.3860202Z","243.0080235","1.00401669999999","10.4651162790698","0.194531126822892","","","","","","","","","" +"2","baseline","244","2026-09-29T02:10:47.3839364Z","244.0058403","0.99781680000001","36.2204724409449","0","","","","","","","","","" +"2","baseline","245","2026-09-29T02:10:48.3835377Z","245.0053338","0.9994935","10.3515625","0","","","","","","","","","" +"2","baseline","246","2026-09-29T02:10:49.3848457Z","246.0066459","1.00131210000001","10.546875","0","","","","","","","","","" +"2","baseline","247","2026-09-29T02:10:50.3872725Z","247.0090561","1.00241019999999","13.8461538461538","0","","","","","","","","","" +"2","baseline","248","2026-09-29T02:10:51.3860556Z","248.0078851","0.998829000000001","12.6953125","0.195541479071993","","","","","","","","","" +"2","baseline","249","2026-09-29T02:10:52.3858831Z","249.0077702","0.9998851","8.87573964497041","0.195334943985064","","","","","","","","","" +"2","baseline","250","2026-09-29T02:10:53.3817279Z","250.0035606","0.995790400000004","8.25147347740668","0","","","","","","","","","" +"2","baseline","251","2026-09-29T02:10:54.3885906Z","251.009981","1.0064204","8.46153846153846","0","","","","","","","","","" +"2","baseline","252","2026-09-29T02:10:55.3897636Z","252.0116303","1.0016493","7.8125","0","","","","","","","","","" +"2","baseline","253","2026-09-29T02:10:56.3853337Z","253.0072347","0.995604400000019","14.0873015873016","0","","","","","","","","","" +"2","baseline","254","2026-09-29T02:10:57.3842762Z","254.0061363","0.998901599999982","14.84375","0","","","","","","","","","" +"2","baseline","255","2026-09-29T02:10:58.3843292Z","255.0062544","1.00011810000001","8.984375","0","","","","","","","","","" +"2","baseline","256","2026-09-29T02:10:59.3864555Z","256.0079352","1.00168079999997","11.7307692307692","0.194984769599263","","","","","","","","","" +"2","baseline","257","2026-09-29T02:11:00.3904613Z","257.0119373","1.00400210000004","9.9609375","0","","","","","","","","","" +"2","baseline","258","2026-09-29T02:11:01.3872497Z","258.0091131","0.99717579999998","5.2734375","0","","","","","","","","","" +"2","baseline","259","2026-09-29T02:11:02.3888545Z","259.0107373","1.00162420000004","8.203125","0.194995787841381","","","","","","","","","" +"2","baseline","260","2026-09-29T02:11:03.3841352Z","260.006088","0.99535069999996","11.9047619047619","0","","","","","","","","","" +"2","baseline","261","2026-09-29T02:11:04.3871873Z","261.0090987","1.0030107","12.5","0","","","","","","","","","" +"2","baseline","262","2026-09-29T02:11:05.3867735Z","262.0085754","0.999476700000002","6.4453125","0","","","","","","","","","" +"2","baseline","263","2026-09-29T02:11:06.3902217Z","263.0121194","1.00354399999998","14.6484375","0","","","","","","","","","" +"2","baseline","264","2026-09-29T02:11:07.3916812Z","264.0135282","1.00140880000004","8.59375","0","","","","","","","","","" +"2","baseline","265","2026-09-29T02:11:08.3938919Z","265.0157585","1.00223030000001","6.25","0","","","","","","","","","" +"2","baseline","266","2026-09-29T02:11:09.3819642Z","266.0037666","0.988008100000002","5.15873015873016","0","","","","","","","","","" +"2","baseline","267","2026-09-29T02:11:10.3810611Z","267.002927","0.999160399999994","5.078125","0","","","","","","","","","" +"2","baseline","268","2026-09-29T02:11:11.3955915Z","268.0176193","1.01469229999998","9.61538461538461","0","","","","","","","","","" +"2","baseline","269","2026-09-29T02:11:12.3792638Z","269.0012855","0.983666200000016","6.54761904761905","0","","","","","","","","","" +"2","baseline","270","2026-09-29T02:11:13.3795992Z","270.0013979","1.00011239999998","6.0546875","0","","","","","","","","","" +"2","baseline","271","2026-09-29T02:11:14.3810173Z","271.0024894","1.00109150000003","5.078125","0","","","","","","","","","" +"2","baseline","272","2026-09-29T02:11:15.3813832Z","272.0033707","1.0008813","9.375","0","","","","","","","","","" +"2","baseline","273","2026-09-29T02:11:16.3956917Z","273.0175134","1.01414269999998","7.30769230769231","0","","","","","","","","","" +"2","baseline","274","2026-09-29T02:11:17.3780774Z","273.9999418","0.982428400000003","10.9126984126984","0","","","","","","","","","" +"2","baseline","275","2026-09-29T02:11:18.3814321Z","275.0032602","1.00331840000001","5.46875","0","","","","","","","","","" +"2","baseline","276","2026-09-29T02:11:19.3857126Z","276.0075767","1.00431650000002","12.890625","0","","","","","","","","","" +"2","baseline","277","2026-09-29T02:11:20.3850132Z","277.006968","0.999391299999957","9.5703125","0.19543145912918","","","","","","","","","" +"2","baseline","278","2026-09-29T02:11:21.3840627Z","278.0058384","0.998870399999987","12.6953125","0","","","","","","","","","" +"2","baseline","279","2026-09-29T02:11:22.3849970Z","279.0069075","1.00106910000005","11.328125","0","","","","","","","","","" +"2","baseline","280","2026-09-29T02:11:23.3847330Z","280.0067705","0.999862999999948","12.109375","0","","","","","","","","","" +"2","baseline","281","2026-09-29T02:11:24.3820095Z","281.0039558","0.997185300000012","4.296875","0","","","","","","","","","" +"2","baseline","282","2026-09-29T02:11:25.3850823Z","282.0070158","1.00306","7.421875","0","","","","","","","","","" +"2","baseline","283","2026-09-29T02:11:26.3867926Z","283.0086696","1.00165379999999","11.5384615384615","0","","","","","","","","","" +"2","baseline","284","2026-09-29T02:11:27.3856449Z","284.007441","0.99877140000001","21.4285714285714","0","","","","","","","","","" +"2","baseline","285","2026-09-29T02:11:28.3843187Z","285.0063298","0.998888800000032","8.984375","0.1955297726834","","","","","","","","","" +"2","baseline","286","2026-09-29T02:11:29.3909458Z","286.0127835","1.00645370000001","6.92307692307692","0.194060094368969","","","","","","","","","" +"2","baseline","287","2026-09-29T02:11:30.3818405Z","287.0037346","0.990951099999961","7.93650793650794","0","","","","","","","","","" +"2","baseline","288","2026-09-29T02:11:31.3835007Z","288.005451","1.00171640000002","6.25","0","","","","","","","","","" +"2","baseline","289","2026-09-29T02:11:32.3847706Z","289.0067152","1.00126419999998","8.0078125","0","","","","","","","","","" +"2","baseline","290","2026-09-29T02:11:33.3838295Z","290.005658","0.998942800000009","5.859375","0","","","","","","","","","" +"2","baseline","291","2026-09-29T02:11:34.3871411Z","291.008983","1.00332500000002","8.46153846153846","0","","","","","","","","","" +"2","baseline","292","2026-09-29T02:11:35.3862259Z","292.0080421","0.999059100000011","11.9842829076621","0","","","","","","","","","" +"2","baseline","293","2026-09-29T02:11:36.3891270Z","293.0111123","1.00307019999997","8.54368932038835","0","","","","","","","","","" +"2","baseline","294","2026-09-29T02:11:37.3890946Z","294.0111077","0.999995399999989","8.7890625","0","","","","","","","","","" +"2","baseline","295","2026-09-29T02:11:38.3874955Z","295.0095797","0.998471999999992","5.6640625","0","","","","","","","","","" +"2","baseline","296","2026-09-29T02:11:39.3948561Z","296.0167585","1.00717880000002","5.6640625","0","","","","","","","","","" +"2","baseline","297","2026-09-29T02:11:40.3853795Z","297.0072148","0.990456300000005","6.34920634920635","0","","","","","","","","","" +"2","baseline","298","2026-09-29T02:11:41.3870686Z","298.00908","1.0018652","6.53846153846154","0","","","","","","","","","" +"2","baseline","299","2026-09-29T02:11:42.3875710Z","299.0094372","1.0003572","10.3515625","0","","","","","","","","","" +"2","baseline","300","2026-09-29T02:11:43.3842708Z","300.0062893","0.996852100000012","12.6984126984127","0","","","","","","","","","" diff --git a/docs/benchmarks/2026-09-29-v0.2.0/results/run-2-block-end.json b/docs/benchmarks/2026-09-29-v0.2.0/results/run-2-block-end.json new file mode 100644 index 0000000..0b844dd --- /dev/null +++ b/docs/benchmarks/2026-09-29-v0.2.0/results/run-2-block-end.json @@ -0,0 +1,12 @@ +{ + "state": "block", + "layers": { + "shutdown": true, + "local_shutdown": true, + "update": false, + "remote": true, + "sleep": true + }, + "uptime_seconds": 333, + "blocked_count": 0 +} diff --git a/docs/benchmarks/2026-09-29-v0.2.0/results/run-2-block-start.json b/docs/benchmarks/2026-09-29-v0.2.0/results/run-2-block-start.json new file mode 100644 index 0000000..6e48522 --- /dev/null +++ b/docs/benchmarks/2026-09-29-v0.2.0/results/run-2-block-start.json @@ -0,0 +1,12 @@ +{ + "state": "block", + "layers": { + "shutdown": true, + "local_shutdown": true, + "update": false, + "remote": true, + "sleep": true + }, + "uptime_seconds": 32, + "blocked_count": 0 +} diff --git a/docs/benchmarks/2026-09-29-v0.2.0/results/run-2-block.csv b/docs/benchmarks/2026-09-29-v0.2.0/results/run-2-block.csv new file mode 100644 index 0000000..fa75c62 --- /dev/null +++ b/docs/benchmarks/2026-09-29-v0.2.0/results/run-2-block.csv @@ -0,0 +1,301 @@ +"run","scenario","sample","utc","elapsed_s","interval_s","system_cpu_pct","collector_cpu_pct","pid","process_cpu_s","cpu_pct","private_mib","working_set_mib","threads","handles","io_read_bytes_s","io_write_bytes_s" +"2","block","1","2026-09-29T02:01:14.3096352Z","1.0083337","1.0083304","15","0","27632","0.015625","0.193698910595178","3.21484375","13.90625","11","194","0","0" +"2","block","2","2026-09-29T02:01:15.3049368Z","2.0035376","0.9952039","8.92857142857143","0","27632","0","0","3.20703125","14.015625","11","194","0","0" +"2","block","3","2026-09-29T02:01:16.3093351Z","3.0079545","1.0044169","9.80769230769231","0","27632","0","0","3.2265625","14.01953125","11","194","0","0" +"2","block","4","2026-09-29T02:01:17.3063188Z","4.0049759","0.9970214","10.3174603174603","0","27632","0","0","3.2265625","14.01953125","11","194","0","0" +"2","block","5","2026-09-29T02:01:18.3031214Z","5.0018518","0.9968759","7.03125","0.195924588005388","27632","0","0","3.2265625","14.01953125","11","194","0","0" +"2","block","6","2026-09-29T02:01:19.3029009Z","6.0015438","0.999692","9.765625","0","27632","0","0","3.2265625","14.01953125","11","194","0","0" +"2","block","7","2026-09-29T02:01:20.3040074Z","7.0026159","1.0010721","7.2265625","0","27632","0","0","3.2265625","14.0234375","11","194","0","0" +"2","block","8","2026-09-29T02:01:21.3057747Z","8.0042593","1.0016434","6.8359375","0","27632","0","0","3.2265625","14.0234375","11","194","0","0" +"2","block","9","2026-09-29T02:01:22.3097257Z","9.0083219","1.0040626","9.03846153846154","0.194522233972264","27632","0","0","3.2265625","14.02734375","11","194","0","0" +"2","block","10","2026-09-29T02:01:23.3148676Z","10.0135912","1.0052693","3.515625","0","27632","0","0","3.2265625","14.02734375","11","194","0","0" +"2","block","11","2026-09-29T02:01:24.3160289Z","11.0146012","1.00101","7.03125","0","27632","0","0","3.2265625","14.02734375","11","194","0","0" +"2","block","12","2026-09-29T02:01:25.3165364Z","12.0153461","1.0007449","6.640625","0","27632","0","0","3.2265625","14.02734375","11","194","0","0" +"2","block","13","2026-09-29T02:01:26.3163739Z","13.0146382","0.999292100000002","6.0546875","0","27632","0","0","3.2265625","14.02734375","11","194","0","0" +"2","block","14","2026-09-29T02:01:27.3051565Z","14.0039815","0.9893433","4.16666666666667","0","27632","0","0","3.2265625","14.02734375","11","194","0","0" +"2","block","15","2026-09-29T02:01:28.3068774Z","15.0054821","1.0015006","14.0625","0.195019853208276","27632","0.015625","0.195019853208276","3.2265625","14.02734375","11","194","0","0" +"2","block","16","2026-09-29T02:01:29.3065769Z","16.0051069","0.999624799999998","13.8671875","0.195385808755446","27632","0","0","3.2265625","14.02734375","11","194","0","0" +"2","block","17","2026-09-29T02:01:30.3048502Z","17.0035658","0.998458900000003","9.765625","0.195613960674795","27632","0","0","3.2265625","14.02734375","11","194","0","0" +"2","block","18","2026-09-29T02:01:31.3084978Z","18.0070947","1.0035289","9.03846153846154","0","27632","0","0","3.2265625","14.03125","11","194","0","0" +"2","block","19","2026-09-29T02:01:32.3071315Z","19.0057088","0.998614100000001","9.32539682539683","0","27632","0","0","3.2265625","14.03515625","11","194","0","0" +"2","block","20","2026-09-29T02:01:33.3037984Z","20.0025311","0.996822299999998","7.8125","0.195935123040486","27632","0","0","3.2265625","14.03515625","11","194","0","0" +"2","block","21","2026-09-29T02:01:34.3036342Z","21.0023279","0.999796799999999","5.859375","0","27632","0","0","3.2265625","14.03515625","11","194","0","0" +"2","block","22","2026-09-29T02:01:35.3034623Z","22.0021165","0.999788600000002","6.25","0","27632","0","0","3.2265625","14.03515625","11","194","0","0" +"2","block","23","2026-09-29T02:01:36.3182093Z","23.017089","1.0149725","6.34615384615385","0","27632","0","0","3.2265625","14.03515625","11","194","0","0" +"2","block","24","2026-09-29T02:01:37.3079706Z","24.0065615","0.989472500000002","11.1545988258317","0.197390528791856","27632","0","0","3.2265625","14.03515625","11","194","0","0" +"2","block","25","2026-09-29T02:01:38.3072420Z","25.0057604","0.9991989","19.4059405940594","0","27632","0","0","3.2265625","14.03515625","11","194","0","0" +"2","block","26","2026-09-29T02:01:39.3062036Z","26.0048608","0.9991004","8.203125","0.195488361329852","27632","0","0","3.2265625","14.03515625","11","194","0","0" +"2","block","27","2026-09-29T02:01:40.3095706Z","27.0081496","1.0032888","9.80769230769231","0.194672261865178","27632","0","0","3.19921875","14.0078125","10","194","0","0" +"2","block","28","2026-09-29T02:01:41.3063641Z","28.0045184","0.996368799999999","8.33333333333333","0","27632","0","0","3.19921875","14.0078125","10","194","0","0" +"2","block","29","2026-09-29T02:01:42.3055467Z","29.0041991","0.999680699999999","6.640625","0.195374883200206","27632","0","0","3.19921875","14.0078125","10","194","0","0" +"2","block","30","2026-09-29T02:01:43.3038329Z","30.0025307","0.9983316","11.1328125","0.195638903947346","27632","0","0","3.19921875","14.0078125","10","194","0","0" +"2","block","31","2026-09-29T02:01:44.3076780Z","31.0065905","1.0040598","8.84615384615385","0.19452277643224","27632","0","0","3.19921875","14.01171875","10","194","0","0" +"2","block","32","2026-09-29T02:01:45.3069415Z","32.0056219","0.999031400000003","10.9126984126984","0","27632","0","0","3.19921875","14.01171875","10","194","0","0" +"2","block","33","2026-09-29T02:01:46.3099301Z","33.0084235","1.0028016","8.84615384615385","0.194766841217645","27632","0","0","3.19921875","14.01171875","10","194","0","0" +"2","block","34","2026-09-29T02:01:47.3093268Z","34.0079278","0.999504299999998","24.609375","0","27632","0","0","3.19921875","14.01171875","10","194","0","0" +"2","block","35","2026-09-29T02:01:48.3110025Z","35.0096225","1.0016947","5.6640625","0","27632","0","0","3.19921875","14.01171875","10","194","0","0" +"2","block","36","2026-09-29T02:01:49.3151306Z","36.0132151","1.0035926","6.4453125","0","27632","0","0","3.19921875","14.01171875","10","194","0","0" +"2","block","37","2026-09-29T02:01:50.3143565Z","37.0129603","0.9997452","6.640625","0","27632","0","0","3.19921875","14.015625","10","194","0","0" +"2","block","38","2026-09-29T02:01:51.3014592Z","38.0001277","0.987167400000004","4.36507936507936","0","27632","0","0","3.19921875","14.015625","10","194","0","0" +"2","block","39","2026-09-29T02:01:52.3044431Z","39.0030206","1.0028929","7.2265625","0","27632","0","0","3.19921875","14.0234375","10","194","0","0" +"2","block","40","2026-09-29T02:01:53.3172810Z","40.0160417","1.0130211","10","0","27632","0","0","3.19921875","14.0234375","10","194","0","0" +"2","block","41","2026-09-29T02:01:54.3159621Z","41.0146851","0.998643400000006","6.4453125","0","27632","0","0","3.2421875","14.02734375","10","194","0","0" +"2","block","42","2026-09-29T02:01:55.3054469Z","42.0036401","0.988954999999997","5.55555555555556","0","27632","0","0","3.2421875","14.02734375","10","194","0","0" +"2","block","43","2026-09-29T02:01:56.3051141Z","43.0036997","1.0000596","8.984375","0.19530086006874","27632","0","0","3.2421875","14.02734375","10","194","0","0" +"2","block","44","2026-09-29T02:01:57.3075516Z","44.0062756","1.0025759","8.7890625","0.194810687150968","27632","0","0","3.2421875","14.02734375","10","194","0","0" +"2","block","45","2026-09-29T02:01:58.3099467Z","45.0086485","1.0023729","7.11538461538461","0.19485014010255","27632","0","0","3.2421875","14.02734375","10","194","0","0" +"2","block","46","2026-09-29T02:01:59.3101345Z","46.0086884","1.0000399","4.6875","0","27632","0","0","3.2421875","14.02734375","10","194","0","0" +"2","block","47","2026-09-29T02:02:00.3176613Z","47.0158303","1.0071419","10.3515625","0","27632","0","0","3.2421875","14.02734375","10","194","0","0" +"2","block","48","2026-09-29T02:02:01.3168946Z","48.0154985","0.999668200000002","7.421875","0","27632","0","0","3.2421875","14.02734375","10","194","0","0" +"2","block","49","2026-09-29T02:02:02.3060438Z","49.004557","0.989058499999999","5.75396825396825","0","27632","0","0","3.2421875","14.03125","10","194","0","0" +"2","block","50","2026-09-29T02:02:03.3110882Z","50.0098001","1.0052431","5","0.194293798186727","27632","0","0","3.2421875","14.03125","10","194","0","0" +"2","block","51","2026-09-29T02:02:04.3073001Z","51.0056039","0.995803799999997","6.34920634920635","0","27632","0","0","3.2421875","14.03125","10","194","0","0" +"2","block","52","2026-09-29T02:02:05.3084382Z","52.0070863","1.0014824","16.3461538461538","0.195023397315819","27632","0","0","3.2421875","14.03125","10","194","0","0" +"2","block","53","2026-09-29T02:02:06.3096230Z","53.0083107","1.0012244","8.7890625","0","27632","0","0","3.2421875","14.03125","10","194","0","0" +"2","block","54","2026-09-29T02:02:07.3168261Z","54.0157027","1.007392","5.078125","0","27632","0","0","3.2421875","14.03125","10","194","0","0" +"2","block","55","2026-09-29T02:02:08.3037071Z","55.002348","0.986645299999999","5.55555555555556","0","27632","0","0","3.2421875","14.03125","10","194","0","0" +"2","block","56","2026-09-29T02:02:09.3067643Z","56.0039084","1.0015604","8.3984375","0","27632","0","0","3.2421875","14.03125","10","194","0","0" +"2","block","57","2026-09-29T02:02:10.3059855Z","57.0047057","1.0007973","8.984375","0.195156901402513","27632","0","0","3.19140625","14.015625","10","194","0","0" +"2","block","58","2026-09-29T02:02:11.3093129Z","58.0076966","1.0029909","14.0384615384615","0.194730081798349","27632","0.015625","0.194730081798349","3.19140625","14.015625","10","194","0","0" +"2","block","59","2026-09-29T02:02:12.3097195Z","59.0083527","1.0006561","8.3984375","0","27632","0","0","3.19140625","14.015625","10","194","0","0" +"2","block","60","2026-09-29T02:02:13.3078417Z","60.0064082","0.9980555","17.6817288801572","0.195693025087282","27632","0.015625","0.195693025087282","3.19140625","14.015625","10","194","0","0" +"2","block","61","2026-09-29T02:02:14.3114487Z","61.0099587","1.0035505","10.4854368932039","0","27632","0","0","3.19140625","14.015625","10","194","0","0" +"2","block","62","2026-09-29T02:02:15.3139088Z","62.0125675","1.0026088","4.296875","0","27632","0","0","3.19140625","14.015625","10","194","0","0" +"2","block","63","2026-09-29T02:02:16.3124605Z","63.0110221","0.998454600000002","5.6640625","0","27632","0","0","3.19140625","14.015625","10","194","0","0" +"2","block","64","2026-09-29T02:02:17.3116750Z","64.010151","0.999128899999995","9.1796875","0","27632","0","0","3.19921875","14.01953125","10","194","0","0" +"2","block","65","2026-09-29T02:02:18.3116108Z","65.010181","1.00003000000001","5.859375","0","27632","0","0","3.19921875","14.01953125","10","194","0","0" +"2","block","66","2026-09-29T02:02:19.3100076Z","66.0086758","0.998494799999989","3.90625","0","27632","0","0","3.19921875","14.01953125","10","194","0","0" +"2","block","67","2026-09-29T02:02:20.3089810Z","67.0076547","0.998978900000012","10.1960784313725","0","27632","0","0","3.19921875","14.0234375","10","194","0","0" +"2","block","68","2026-09-29T02:02:21.3181047Z","68.0168297","1.009175","9.53307392996109","0","27632","0","0","3.19921875","14.0234375","10","194","0","0" +"2","block","69","2026-09-29T02:02:22.3172893Z","69.0155629","0.99873319999999","6.0546875","0","27632","0","0","3.19921875","14.0234375","10","194","0","0" +"2","block","70","2026-09-29T02:02:23.3163438Z","70.0145336","0.998970700000001","6.640625","0","27632","0","0","3.19921875","14.0234375","10","194","0","0" +"2","block","71","2026-09-29T02:02:24.3147781Z","71.0135304","0.9989968","4.4921875","0","27632","0","0","3.2421875","14.02734375","10","194","0","0" +"2","block","72","2026-09-29T02:02:25.3136912Z","72.0117559","0.998225500000004","7.6171875","0","27632","0","0","3.2421875","14.02734375","10","194","0","0" +"2","block","73","2026-09-29T02:02:26.3137393Z","73.0123324","1.00057649999999","5.2734375","0","27632","0","0","3.2421875","14.02734375","10","194","0","0" +"2","block","74","2026-09-29T02:02:27.3179684Z","74.0165122","1.0041798","4.8828125","0","27632","0","0","3.2421875","14.03125","10","194","0","0" +"2","block","75","2026-09-29T02:02:28.3089120Z","75.0075483","0.991036100000002","13.28125","0","27632","0","0","3.2421875","14.03125","10","194","0","0" +"2","block","76","2026-09-29T02:02:29.3084467Z","76.0069525","0.999404200000001","13.671875","0","27632","0","0","3.2421875","14.03125","10","194","0","0" +"2","block","77","2026-09-29T02:02:30.3301301Z","77.01716","1.01020749999999","34.5631067961165","0","27632","0.015625","0.193338992236745","3.2421875","14.03125","10","194","0","0" +"2","block","78","2026-09-29T02:02:31.3109924Z","78.0097054","0.992545400000012","26.3261296660118","0","27632","0","0","3.2421875","14.03515625","10","194","0","0" +"2","block","79","2026-09-29T02:02:32.3068910Z","79.0055802","0.995874799999996","18.0555555555556","0","27632","0","0","3.2421875","14.03515625","10","194","0","0" +"2","block","80","2026-09-29T02:02:33.3076528Z","80.0062531","1.0006729","15.0390625","0.19518116259569","27632","0","0","3.2421875","14.03515625","10","194","0","0" +"2","block","81","2026-09-29T02:02:34.3174311Z","81.0160837","1.0098306","5.76923076923077","0.193411152325944","27632","0","0","3.2421875","14.03515625","10","194","0","0" +"2","block","82","2026-09-29T02:02:35.3039232Z","82.0024675","0.986383799999999","5.35714285714286","0","27632","0","0","3.2421875","14.0390625","10","194","0","0" +"2","block","83","2026-09-29T02:02:36.3033469Z","83.0020628","0.999595299999996","6.0546875","0","27632","0","0","3.2421875","14.0390625","10","194","0","0" +"2","block","84","2026-09-29T02:02:37.3038580Z","84.0025932","1.0005304","8.59375","0","27632","0","0","3.2421875","14.04296875","10","194","0","0" +"2","block","85","2026-09-29T02:02:38.3153361Z","85.0139877","1.01139450000001","7.5","0","27632","0","0","3.2421875","14.04296875","10","194","0","0" +"2","block","86","2026-09-29T02:02:39.3161584Z","86.0153879","1.00140019999999","4.6875","0","27632","0","0","3.2421875","14.04296875","10","194","0","0" +"2","block","87","2026-09-29T02:02:40.3172056Z","87.0157332","1.00034530000001","9.375","0","27632","0","0","3.2421875","14.02734375","10","194","0","0" +"2","block","88","2026-09-29T02:02:41.3142808Z","88.0129958","0.997262599999999","6.8359375","0","27632","0","0","3.2421875","14.02734375","10","194","0","0" +"2","block","89","2026-09-29T02:02:42.3028008Z","89.0015292","0.988533399999994","3.96825396825397","0","27632","0","0","3.2421875","14.02734375","10","194","0","0" +"2","block","90","2026-09-29T02:02:43.3053638Z","90.0039452","1.002416","7.2265625","0","27632","0","0","3.2421875","14.02734375","10","194","0","0" +"2","block","91","2026-09-29T02:02:44.3061944Z","91.0043921","1.0004469","17.578125","0.195225253834062","27632","0","0","3.2421875","14.02734375","10","194","0","0" +"2","block","92","2026-09-29T02:02:45.3074220Z","92.0056124","1.0012203","14.453125","0.195074450647874","27632","0.015625","0.195074450647874","3.2421875","14.02734375","10","194","0","0" +"2","block","93","2026-09-29T02:02:46.3123639Z","93.0110175","1.0054051","7.88461538461539","0.194262491805541","27632","0","0","3.2421875","14.02734375","10","194","0","0" +"2","block","94","2026-09-29T02:02:47.3083377Z","94.0069861","0.995968599999998","7.03125","0","27632","0","0","3.2421875","14.03125","10","194","0","0" +"2","block","95","2026-09-29T02:02:48.3103003Z","95.0090486","1.00206250000001","7.03125","0","27632","0","0","3.2421875","14.03125","10","194","0","0" +"2","block","96","2026-09-29T02:02:49.3087706Z","96.0074284","0.998379799999995","4.8828125","0","27632","0","0","3.2421875","14.03125","10","194","0","0" +"2","block","97","2026-09-29T02:02:50.3068440Z","97.0054743","0.998045899999994","12.1031746031746","0","27632","0","0","3.2421875","14.03125","10","194","0","0" +"2","block","98","2026-09-29T02:02:51.3073166Z","98.0059861","1.00051180000001","12.3046875","0.195212590196335","27632","0","0","3.2421875","14.03125","10","194","0","0" +"2","block","99","2026-09-29T02:02:52.3091111Z","99.0077275","1.0017414","17.3076923076923","0.194972974062967","27632","0","0","3.2421875","14.03125","10","194","0","0" +"2","block","100","2026-09-29T02:02:53.3086224Z","100.0072655","0.999537999999987","16.6015625","0","27632","0.015625","0.195402776082553","3.2421875","14.03125","10","194","0","0" +"2","block","101","2026-09-29T02:02:54.3086263Z","101.0073089","1.00004340000001","7.6171875","0","27632","0","0","3.2421875","14.03125","10","194","0","0" +"2","block","102","2026-09-29T02:02:55.3118547Z","102.0105286","1.0032197","4.8828125","0","27632","0","0","3.21875","14.01953125","9","192","0","0" +"2","block","103","2026-09-29T02:02:56.3172891Z","103.0159152","1.00538659999999","6.8359375","0","27632","0","0","3.21875","14.01953125","9","192","0","0" +"2","block","104","2026-09-29T02:02:57.3164067Z","104.0150482","0.999133","6.8359375","0","27632","0","0","3.21875","14.01953125","9","192","0","0" +"2","block","105","2026-09-29T02:02:58.3038840Z","105.0026625","0.987614300000004","5.75396825396825","0","27632","0","0","3.21875","14.01953125","9","192","0","0" +"2","block","106","2026-09-29T02:02:59.3055023Z","106.004149","1.0014865","7.03125","0","27632","0","0","3.21875","14.01953125","9","192","0","0" +"2","block","107","2026-09-29T02:03:00.3108199Z","107.0090209","1.0048719","7.11538461538461","0.194365570377677","27632","0","0","3.21875","14.01953125","9","192","0","0" +"2","block","108","2026-09-29T02:03:01.3108759Z","108.0095034","1.0004825","9.5703125","0","27632","0","0","3.21875","14.01953125","9","192","0","0" +"2","block","109","2026-09-29T02:03:02.3067840Z","109.0054786","0.99597519999999","8.33333333333333","0","27632","0","0","3.21875","14.01953125","9","192","0","0" +"2","block","110","2026-09-29T02:03:03.3057421Z","110.004424","0.998945400000011","7.2265625","0.195518694014706","27632","0","0","3.21875","14.0234375","9","192","0","0" +"2","block","111","2026-09-29T02:03:04.3060740Z","111.0046779","1.00025389999999","9.5703125","0.195262922743917","27632","0","0","3.21875","14.02734375","9","192","0","0" +"2","block","112","2026-09-29T02:03:05.3027680Z","112.001454","0.996776100000005","8.203125","0.195944204520954","27632","0","0","3.21875","14.02734375","9","192","0","0" +"2","block","113","2026-09-29T02:03:06.3043700Z","113.0030283","1.0015743","5.2734375","0","27632","0","0","3.21875","14.02734375","9","192","0","0" +"2","block","114","2026-09-29T02:03:07.3067751Z","114.0050796","1.00205129999999","6.25","0","27632","0","0","3.21875","14.02734375","9","192","0","0" +"2","block","115","2026-09-29T02:03:08.3039522Z","115.0025676","0.997488000000004","8.3984375","0.19580436055371","27632","0","0","3.21875","14.02734375","9","192","0","0" +"2","block","116","2026-09-29T02:03:09.3152247Z","116.0138115","1.0112439","6.73076923076923","0","27632","0","0","3.21875","14.02734375","9","192","0","0" +"2","block","117","2026-09-29T02:03:10.3152955Z","117.0139414","1.0001299","5.46875","0","27632","0","0","3.203125","14.0234375","10","192","0","0" +"2","block","118","2026-09-29T02:03:11.3141661Z","118.0127453","0.998803899999999","10.15625","0","27632","0","0","3.203125","14.0234375","10","192","0","0" +"2","block","119","2026-09-29T02:03:12.3053151Z","119.0039233","0.991178000000005","6.54761904761905","0","27632","0","0","3.203125","14.0234375","10","192","0","0" +"2","block","120","2026-09-29T02:03:13.3041475Z","120.0027849","0.998861599999998","13.4765625","0.195535097154601","27632","0","0","3.203125","14.0234375","10","192","0","0" +"2","block","121","2026-09-29T02:03:14.3034372Z","121.0021201","0.999335200000004","6.8359375","0","27632","0","0","3.203125","14.0234375","10","192","0","0" +"2","block","122","2026-09-29T02:03:15.3070929Z","122.0056963","1.0035762","7.2265625","0","27632","0","0","3.203125","14.02734375","10","192","0","0" +"2","block","123","2026-09-29T02:03:16.3066038Z","123.0052069","0.999510599999994","16.2109375","0.195408132740164","27632","0.015625","0.195408132740164","3.203125","14.02734375","10","192","0","0" +"2","block","124","2026-09-29T02:03:17.3047159Z","124.0032717","0.998064800000009","8.0078125","0.19569120161336","27632","0","0","3.203125","14.02734375","10","192","0","0" +"2","block","125","2026-09-29T02:03:18.3070952Z","125.0057378","1.00246609999999","6.25","0.194832024743781","27632","0","0","3.203125","14.02734375","10","192","0","0" +"2","block","126","2026-09-29T02:03:19.3104963Z","126.0091606","1.00342280000001","6.92307692307692","0.194646264764961","27632","0","0","3.203125","14.02734375","10","192","0","0" +"2","block","127","2026-09-29T02:03:20.3116120Z","127.0101649","1.00100429999999","6.640625","0","27632","0","0","3.24609375","14.03125","10","192","0","0" +"2","block","128","2026-09-29T02:03:21.3062020Z","128.0048873","0.994722400000015","15.6746031746032","0","27632","0","0","3.24609375","14.03125","10","192","0","0" +"2","block","129","2026-09-29T02:03:22.3037241Z","129.0022362","0.997348899999992","12.109375","0.195831669338585","27632","0","0","3.24609375","14.03515625","10","192","0","0" +"2","block","130","2026-09-29T02:03:23.3052849Z","130.0038497","1.00161349999999","6.25","0","27632","0","0","3.24609375","14.03515625","10","192","0","0" +"2","block","131","2026-09-29T02:03:24.3043431Z","131.0029371","0.999087400000008","6.4453125","0.195490904999901","27632","0","0","3.24609375","14.03515625","10","192","0","0" +"2","block","132","2026-09-29T02:03:25.3044445Z","132.0030027","1.0000656","9.375","0","27632","0","0","3.24609375","14.03515625","10","192","0","0" +"2","block","133","2026-09-29T02:03:26.3044495Z","133.0030085","1.0000058","6.25","0","27632","0","0","3.24609375","14.03515625","10","192","0","0" +"2","block","134","2026-09-29T02:03:27.3034435Z","134.0020481","0.999039600000003","4.6875","0","27632","0","0","3.24609375","14.03515625","10","192","0","0" +"2","block","135","2026-09-29T02:03:28.3067482Z","135.0052734","1.0032253","8.0078125","0","27632","0","0","3.24609375","14.03515625","10","192","0","0" +"2","block","136","2026-09-29T02:03:29.3081426Z","136.0062797","1.0010063","10.7421875","0.195116154613612","27632","0.015625","0.195116154613612","3.24609375","14.0390625","10","192","0","0" +"2","block","137","2026-09-29T02:03:30.3088495Z","137.0076195","1.00133980000001","13.0769230769231","0.19505117044184","27632","0","0","3.24609375","14.0390625","10","192","0","0" +"2","block","138","2026-09-29T02:03:31.3112246Z","138.0099929","1.00237339999998","10.15625","0","27632","0","0","3.24609375","14.0390625","10","192","0","0" +"2","block","139","2026-09-29T02:03:32.3121742Z","139.010718","1.00072510000001","6.25","0","27632","0","0","3.24609375","14.0390625","10","192","0","0" +"2","block","140","2026-09-29T02:03:33.3123899Z","140.0110021","1.00028409999999","6.640625","0","27632","0","0","3.24609375","14.0390625","10","192","0","0" +"2","block","141","2026-09-29T02:03:34.3147300Z","141.0132921","1.00229000000002","4.4921875","0","27632","0","0","3.24609375","14.0390625","10","192","0","0" +"2","block","142","2026-09-29T02:03:35.3019111Z","142.0005387","0.987246599999992","6.74603174603175","0","27632","0","0","3.24609375","14.0390625","10","192","0","0" +"2","block","143","2026-09-29T02:03:36.3035231Z","143.0022992","1.00176049999999","13.28125","0","27632","0","0","3.24609375","14.0390625","10","192","0","0" +"2","block","144","2026-09-29T02:03:37.3063296Z","144.0049645","1.00266530000002","9.9609375","0","27632","0","0","3.24609375","14.0390625","10","192","0","0" +"2","block","145","2026-09-29T02:03:38.3071041Z","145.005328","1.00036349999999","9.765625","0.195241529703954","27632","0","0","3.24609375","14.0390625","10","192","0","0" +"2","block","146","2026-09-29T02:03:39.3080911Z","146.0066332","1.00130519999999","15.3846153846154","0.195057910415328","27632","0","0","3.24609375","14.0390625","10","192","0","0" +"2","block","147","2026-09-29T02:03:40.3067040Z","147.0055821","0.998948900000016","15.2777777777778","0","27632","0","0","3.203125","14.0234375","10","194","0","0" +"2","block","148","2026-09-29T02:03:41.3060573Z","148.0046725","0.9990904","16.015625","0.195490317993247","27632","0","0","3.203125","14.0234375","10","194","0","0" +"2","block","149","2026-09-29T02:03:42.3045198Z","149.0031876","0.998515099999992","6.25","0.195602950821677","27632","0","0","3.203125","14.0234375","10","194","0","0" +"2","block","150","2026-09-29T02:03:43.3087878Z","150.007383","1.00419540000001","11.5384615384615","0.194496509344692","27632","0","0","3.203125","14.0234375","10","194","0","0" +"2","block","151","2026-09-29T02:03:44.3083589Z","151.0069902","0.999607199999986","9.1796875","0","27632","0","0","3.203125","14.02734375","10","194","0","0" +"2","block","152","2026-09-29T02:03:45.3050014Z","152.003543","0.996552799999989","12.8968253968254","0","27632","0","0","3.203125","14.02734375","10","194","0","0" +"2","block","153","2026-09-29T02:03:46.3182299Z","153.0169113","1.01336830000002","4.23076923076923","0.192735948025999","27632","0","0","3.203125","14.02734375","10","194","0","0" +"2","block","154","2026-09-29T02:03:47.3029563Z","154.0015244","0.98461309999999","6.15079365079365","0","27632","0","0","3.203125","14.02734375","10","194","0","0" +"2","block","155","2026-09-29T02:03:48.3174898Z","155.0162556","1.0147312","7.11538461538461","0","27632","0","0","3.203125","14.02734375","10","194","0","0" +"2","block","156","2026-09-29T02:03:49.3084245Z","156.0070337","0.9907781","6.4453125","0","27632","0","0","3.203125","14.02734375","10","194","0","0" +"2","block","157","2026-09-29T02:03:50.3112027Z","157.009302","1.0022683","11.328125","0","27632","0","0","3.203125","14.02734375","10","194","0","0" +"2","block","158","2026-09-29T02:03:51.3084724Z","158.007047","0.997745000000009","7.8125","0","27632","0","0","3.203125","14.02734375","10","194","0","0" +"2","block","159","2026-09-29T02:03:52.3101696Z","159.0088374","1.0017904","8.7890625","0","27632","0","0","3.203125","14.02734375","10","194","0","0" +"2","block","160","2026-09-29T02:03:53.3066842Z","160.0052926","0.996455199999986","15.0793650793651","0","27632","0","0","3.24609375","14.03125","10","194","0","0" +"2","block","161","2026-09-29T02:03:54.3032464Z","161.001902","0.996609400000011","6.640625","0.195976979546849","27632","0","0","3.24609375","14.03125","10","194","0","0" +"2","block","162","2026-09-29T02:03:55.3073014Z","162.0058749","1.00397289999998","6.26223091976517","0","27632","0","0","3.24609375","14.03125","10","194","0","0" +"2","block","163","2026-09-29T02:03:56.3068548Z","163.0054454","0.999570500000004","11.8908382066277","0.195396422763576","27632","0.015625","0.195396422763576","3.24609375","14.03125","10","194","0","0" +"2","block","164","2026-09-29T02:03:57.3060473Z","164.0048779","0.999432500000012","10.15625","0.195423402781076","27632","0","0","3.24609375","14.03125","10","194","0","0" +"2","block","165","2026-09-29T02:03:58.3040090Z","165.0026724","0.997794499999998","5.46875","0.195744213863677","27632","0","0","3.24609375","14.03125","10","194","0","0" +"2","block","166","2026-09-29T02:03:59.3038171Z","166.0026788","1.00000639999999","4.6875","0","27632","0","0","3.24609375","14.03125","10","194","0","0" +"2","block","167","2026-09-29T02:04:00.3037199Z","167.0022951","0.999616300000014","7.2265625","0.195387470172302","27632","0","0","3.24609375","14.03125","10","194","0","0" +"2","block","168","2026-09-29T02:04:01.3057669Z","168.0044688","1.00217369999999","13.671875","0","27632","0","0","3.24609375","14.03125","10","194","0","0" +"2","block","169","2026-09-29T02:04:02.3046467Z","169.0033721","0.998903299999995","4.4921875","0.195526934388945","27632","0","0","3.24609375","14.03125","10","194","0","0" +"2","block","170","2026-09-29T02:04:03.3069362Z","170.0055313","1.00215920000002","5.6640625","0","27632","0","0","3.24609375","14.03515625","10","194","0","0" +"2","block","171","2026-09-29T02:04:04.3115563Z","171.0102233","1.00469199999998","10.1923076923077","0.194400373447787","27632","0","0","3.24609375","14.03515625","10","194","0","0" +"2","block","172","2026-09-29T02:04:05.3157949Z","172.0144414","1.0042181","8.3984375","0","27632","0","0","3.24609375","14.03515625","10","194","0","0" +"2","block","173","2026-09-29T02:04:06.3149733Z","173.0136625","0.9992211","5.078125","0","27632","0","0","3.24609375","14.03515625","10","194","0","0" +"2","block","174","2026-09-29T02:04:07.3155539Z","174.0141306","1.00046810000001","7.8125","0","27632","0","0","3.24609375","14.03515625","10","194","0","0" +"2","block","175","2026-09-29T02:04:08.3113207Z","175.0104363","0.996305699999994","18.1640625","0","27632","0","0","3.24609375","14.03515625","10","194","0","0" +"2","block","176","2026-09-29T02:04:09.3108889Z","176.0094566","0.999020300000012","19.53125","0","27632","0","0","3.24609375","14.03515625","10","194","0","0" +"2","block","177","2026-09-29T02:04:10.3119707Z","177.0105696","1.001113","8.203125","0","27632","0","0","3.1953125","14.01953125","10","194","0","0" +"2","block","178","2026-09-29T02:04:11.3111577Z","178.0097902","0.999220600000001","33.3984375","0","27632","0","0","3.1953125","14.01953125","10","194","0","0" +"2","block","179","2026-09-29T02:04:12.3104442Z","179.0090165","0.999226300000004","8.203125","0","27632","0","0","3.1953125","14.01953125","10","194","0","0" +"2","block","180","2026-09-29T02:04:13.3127303Z","180.0113849","1.00236839999999","7.421875","0","27632","0","0","3.1953125","14.01953125","10","194","0","0" +"2","block","181","2026-09-29T02:04:14.3090464Z","181.0076916","0.996306699999991","8.3984375","0","27632","0","0","3.1953125","14.01953125","10","194","0","0" +"2","block","182","2026-09-29T02:04:15.3108172Z","182.0093681","1.0016765","12.5","0","27632","0","0","3.1953125","14.01953125","10","194","0","0" +"2","block","183","2026-09-29T02:04:16.3091487Z","183.0078115","0.998443400000014","19.7265625","0","27632","0","0","3.1953125","14.01953125","10","194","0","0" +"2","block","184","2026-09-29T02:04:17.3058087Z","184.0044189","0.996607399999988","41.468253968254","0","27632","0","0","3.203125","14.02734375","10","194","0","0" +"2","block","185","2026-09-29T02:04:18.3180862Z","185.0166881","1.01226919999999","29.4230769230769","0.192945216549117","27632","0","0","3.203125","14.02734375","10","194","0","0" +"2","block","186","2026-09-29T02:04:19.3163669Z","186.0145369","0.997848800000014","8.3984375","0","27632","0","0","3.203125","13.0234375","10","194","0","0" +"2","block","187","2026-09-29T02:04:20.3118810Z","187.0103997","0.995862799999998","6.8359375","0","27632","0","0","3.24609375","13.02734375","10","194","0","0" +"2","block","188","2026-09-29T02:04:21.3085846Z","188.0070958","0.996696100000008","24.0234375","0","27632","0","0","3.24609375","13.02734375","10","194","0","0" +"2","block","189","2026-09-29T02:04:22.3111546Z","189.0103902","1.00329439999999","27.734375","0","27632","0","0","3.24609375","13.02734375","10","194","0","0" +"2","block","190","2026-09-29T02:04:23.3029384Z","190.0015818","0.991191600000008","32.9365079365079","0","27632","0.015625","0.197048179181501","3.24609375","13.02734375","10","194","0","0" +"2","block","191","2026-09-29T02:04:24.3053090Z","191.0039867","1.00240489999999","10.7421875","0.194843919857138","27632","0","0","3.24609375","13.03125","10","194","0","0" +"2","block","192","2026-09-29T02:04:25.3075927Z","192.0061089","1.0021222","16.9921875","0.194898885585011","27632","0","0","3.24609375","13.03125","10","194","0","0" +"2","block","193","2026-09-29T02:04:26.3053421Z","193.0040344","0.997925500000008","16.9921875","0.195718518065726","27632","0","0","3.24609375","13.03515625","10","194","0","0" +"2","block","194","2026-09-29T02:04:27.3041365Z","194.0027823","0.998747899999984","21.484375","0.195557357367163","27632","0","0","3.24609375","13.04296875","10","194","0","0" +"2","block","195","2026-09-29T02:04:28.3144800Z","195.0129837","1.0102014","39.6153846153846","0","27632","0","0","3.24609375","13.04296875","10","194","0","0" +"2","block","196","2026-09-29T02:04:29.3091520Z","196.0077838","0.99480010000002","27.734375","0","27632","0","0","3.24609375","13.04296875","10","194","0","0" +"2","block","197","2026-09-29T02:04:30.3047679Z","197.0033938","0.995609999999999","19.2460317460317","0","27632","0","0","3.24609375","13.04296875","10","194","0","0" +"2","block","198","2026-09-29T02:04:31.3151685Z","198.0138183","1.0104245","6.34615384615385","0.193297470518579","27632","0","0","3.24609375","13.04296875","10","194","0","0" +"2","block","199","2026-09-29T02:04:32.3147967Z","199.0134069","0.999588599999981","13.0859375","0","27632","0","0","3.24609375","13.04296875","10","194","0","0" +"2","block","200","2026-09-29T02:04:33.3149908Z","200.013654","1.00024710000002","5.859375","0","27632","0","0","3.24609375","13.04296875","10","194","0","0" +"2","block","201","2026-09-29T02:04:34.3026618Z","201.0019314","0.988277399999987","6.54761904761905","0","27632","0","0","3.24609375","13.04296875","10","194","0","0" +"2","block","202","2026-09-29T02:04:35.3163493Z","202.0149696","1.01303820000001","6.15384615384615","0","27632","0","0","3.24609375","13.04296875","10","194","0","0" +"2","block","203","2026-09-29T02:04:36.3053791Z","203.0041615","0.98919189999998","8.92857142857143","0","27632","0","0","3.24609375","13.04296875","10","194","0","0" +"2","block","204","2026-09-29T02:04:37.3165825Z","204.0153452","1.0111837","8.65384615384615","0.193152342151084","27632","0","0","3.24609375","13.04296875","10","194","0","0" +"2","block","205","2026-09-29T02:04:38.3172370Z","205.0160357","1.00069050000002","10.15625","0","27632","0","0","3.24609375","13.04296875","10","194","0","0" +"2","block","206","2026-09-29T02:04:39.3165872Z","206.015099","0.999063299999989","6.4453125","0","27632","0","0","3.24609375","13.04296875","10","194","0","0" +"2","block","207","2026-09-29T02:04:40.3168617Z","207.0154292","1.00033020000001","5.859375","0","27632","0","0","3.24609375","13.03515625","10","194","0","0" +"2","block","208","2026-09-29T02:04:41.3164546Z","208.0151546","0.999725399999988","5.2734375","0","27632","0","0","3.24609375","13.03515625","10","194","0","0" +"2","block","209","2026-09-29T02:04:42.3029060Z","209.0015446","0.98639","9.52380952380952","0","27632","0","0","3.24609375","13.03515625","10","194","0","0" +"2","block","210","2026-09-29T02:04:43.3159348Z","210.0144643","1.0129197","5.96153846153846","0","27632","0","0","3.24609375","13.03515625","10","194","0","0" +"2","block","211","2026-09-29T02:04:44.3013220Z","210.9999558","0.985491499999995","9.12698412698413","0","27632","0","0","3.24609375","13.03515625","10","194","0","0" +"2","block","212","2026-09-29T02:04:45.3120900Z","212.0108842","1.01092840000001","9.42307692307692","0","27632","0","0","3.24609375","13.03515625","10","194","0","0" +"2","block","213","2026-09-29T02:04:46.3105234Z","213.0090986","0.998214399999995","5.6640625","0","27632","0","0","3.24609375","13.03515625","10","194","0","0" +"2","block","214","2026-09-29T02:04:47.3074907Z","214.0060669","0.996968299999992","13.8888888888889","0","27632","0","0","3.24609375","13.0390625","10","194","0","0" +"2","block","215","2026-09-29T02:04:48.3100971Z","215.0086896","1.00262270000002","12.5","0.194801593859781","27632","0","0","3.24609375","13.0390625","10","194","0","0" +"2","block","216","2026-09-29T02:04:49.3093857Z","216.0081461","0.99945649999998","10.546875","0","27632","0","0","3.24609375","13.0390625","10","194","0","0" +"2","block","217","2026-09-29T02:04:50.3125990Z","217.0118865","1.00374040000003","7.421875","0","27632","0","0","3.24609375","13.0390625","10","194","0","0" +"2","block","218","2026-09-29T02:04:51.3142404Z","218.0128857","1.0009992","4.6875","0","27632","0","0","3.24609375","13.0390625","10","194","0","0" +"2","block","219","2026-09-29T02:04:52.3161476Z","219.014798","1.00191229999999","10.9375","0","27632","0","0","3.24609375","13.0390625","10","194","0","0" +"2","block","220","2026-09-29T02:04:53.3150220Z","220.013805","0.999007000000006","5.859375","0","27632","0","0","3.24609375","13.0390625","10","194","0","0" +"2","block","221","2026-09-29T02:04:54.3152220Z","221.0137687","0.999963699999995","4.1015625","0","27632","0","0","3.24609375","13.0390625","10","194","0","0" +"2","block","222","2026-09-29T02:04:55.3134387Z","222.0120419","0.9982732","4.4921875","0","27632","0","0","3.24609375","13.0390625","10","194","0","0" +"2","block","223","2026-09-29T02:04:56.3160986Z","223.0149563","1.00291440000001","8.3984375","0","27632","0","0","3.24609375","13.0390625","10","194","0","0" +"2","block","224","2026-09-29T02:04:57.3180995Z","224.017128","1.00217169999999","5.2734375","0","27632","0","0","3.24609375","13.04296875","10","194","0","0" +"2","block","225","2026-09-29T02:04:58.3178488Z","225.0166566","0.999528599999991","7.2265625","0","27632","0","0","3.24609375","13.04296875","10","194","0","0" +"2","block","226","2026-09-29T02:04:59.3059710Z","226.004644","0.987987400000009","6.54761904761905","0","27632","0","0","3.24609375","13.046875","10","194","0","0" +"2","block","227","2026-09-29T02:05:00.3080551Z","227.0063133","1.0016693","9.45945945945946","0.389974016374465","27632","0","0","3.24609375","13.046875","10","194","0","0" +"2","block","228","2026-09-29T02:05:01.3066306Z","228.005293","0.998979700000007","14.6245059288538","0","27632","0","0","3.24609375","13.046875","10","194","0","0" +"2","block","229","2026-09-29T02:05:02.3047371Z","229.0034025","0.998109499999998","27.1484375","0.195682437648375","27632","0","0","3.24609375","13.046875","10","194","0","0" +"2","block","230","2026-09-29T02:05:03.3080832Z","230.0066582","1.00325569999998","17.1153846153846","0","27632","0","0","3.24609375","13.046875","10","194","0","0" +"2","block","231","2026-09-29T02:05:04.3177904Z","231.0163828","1.00972460000003","9.375","0","27632","0","0","3.24609375","13.046875","10","194","0","0" +"2","block","232","2026-09-29T02:05:05.3045664Z","232.0032573","0.986874499999999","6.74603174603175","0","27632","0","0","3.24609375","13.046875","10","194","0","0" +"2","block","233","2026-09-29T02:05:06.3044291Z","233.0031124","0.999855099999991","5.2734375","0.195340804882629","27632","0","0","3.24609375","13.046875","10","194","0","0" +"2","block","234","2026-09-29T02:05:07.3073084Z","234.0060545","1.00294209999998","19.921875","0.194739556750089","27632","0","0","3.24609375","13.046875","10","194","0","0" +"2","block","235","2026-09-29T02:05:08.3122407Z","235.0109438","1.0048893","8.65384615384615","0.19436220487172","27632","0","0","3.24609375","13.046875","10","194","0","0" +"2","block","236","2026-09-29T02:05:09.3139672Z","236.012548","1.0016042","7.03125","0","27632","0","0","3.21484375","13.03125","9","192","0","0" +"2","block","237","2026-09-29T02:05:10.3158309Z","237.0143827","1.00183470000002","4.8828125","0","27632","0","0","3.24609375","13.03125","10","192","0","0" +"2","block","238","2026-09-29T02:05:11.3151329Z","238.013646","0.999263299999996","7.421875","0","27632","0","0","3.24609375","13.03125","10","192","0","0" +"2","block","239","2026-09-29T02:05:12.3046554Z","239.0032091","0.989563099999998","9.74155069582505","0","27632","0","0","3.24609375","13.03125","10","192","0","0" +"2","block","240","2026-09-29T02:05:13.3168519Z","240.0154578","1.01224869999999","8.82917466410749","0.192949124064079","27632","0","0","3.24609375","13.03125","10","192","0","0" +"2","block","241","2026-09-29T02:05:14.3174941Z","241.0162383","1.00078050000002","5.6640625","0","27632","0","0","3.24609375","13.03125","10","192","0","0" +"2","block","242","2026-09-29T02:05:15.3081130Z","242.0067936","0.990555299999983","7.2265625","0","27632","0","0","3.24609375","13.03515625","10","192","0","0" +"2","block","243","2026-09-29T02:05:16.3111798Z","243.0098973","1.0031037","8.7890625","0","27632","0","0","3.24609375","13.03515625","10","192","0","0" +"2","block","244","2026-09-29T02:05:17.3081774Z","244.006739","0.996841700000004","10.9375","0","27632","0","0","3.24609375","13.03515625","10","192","0","0" +"2","block","245","2026-09-29T02:05:18.3172654Z","245.0157816","1.00904260000001","11.328125","0","27632","0","0","3.24609375","13.03515625","10","192","0","0" +"2","block","246","2026-09-29T02:05:19.3181354Z","246.0169163","1.00113469999999","6.25","0","27632","0","0","3.24609375","13.03515625","10","192","0","0" +"2","block","247","2026-09-29T02:05:20.3114999Z","247.0100781","0.993161799999996","11.328125","0","27632","0","0","3.24609375","13.03515625","10","192","0","0" +"2","block","248","2026-09-29T02:05:21.3123628Z","248.0110251","1.000947","7.03125","0","27632","0","0","3.24609375","13.03515625","10","192","0","0" +"2","block","249","2026-09-29T02:05:22.3104508Z","249.0090291","0.998004000000009","8.984375","0","27632","0","0","3.24609375","13.03515625","10","192","0","0" +"2","block","250","2026-09-29T02:05:23.3163848Z","250.0150439","1.0060148","6.0546875","0","27632","0","0","3.24609375","13.0390625","10","192","0","0" +"2","block","251","2026-09-29T02:05:24.3036154Z","251.0023251","0.987281199999984","5.75396825396825","0","27632","0","0","3.24609375","13.0390625","10","192","0","0" +"2","block","252","2026-09-29T02:05:25.3064567Z","252.0050958","1.00277070000001","7.8125","0","27632","0","0","3.24609375","13.04296875","10","192","0","0" +"2","block","253","2026-09-29T02:05:26.3058041Z","253.0045717","0.999475899999993","12.6953125","0.195414916957979","27632","0","0","3.24609375","13.04296875","10","192","0","0" +"2","block","254","2026-09-29T02:05:27.3048036Z","254.0033846","0.99881289999999","5.078125","0.195544631031499","27632","0","0","3.24609375","13.04296875","10","192","0","0" +"2","block","255","2026-09-29T02:05:28.3039807Z","255.0025441","0.999159500000019","4.4921875","0.195476798248925","27632","0","0","3.24609375","13.04296875","10","192","0","0" +"2","block","256","2026-09-29T02:05:29.3179822Z","256.0165714","1.01402729999998","7.30769230769231","0","27632","0","0","3.24609375","13.04296875","10","192","0","0" +"2","block","257","2026-09-29T02:05:30.3155660Z","257.0142961","0.997724699999992","7.2265625","0","27632","0","0","3.24609375","13.046875","10","192","0","0" +"2","block","258","2026-09-29T02:05:31.3127507Z","258.0112167","0.99692060000001","6.8359375","0","27632","0","0","3.24609375","13.046875","10","192","0","0" +"2","block","259","2026-09-29T02:05:32.3108250Z","259.0094029","0.998186200000021","13.4765625","0","27632","0","0","3.24609375","13.046875","10","192","0","0" +"2","block","260","2026-09-29T02:05:33.3075065Z","260.0061075","0.996704599999987","12.1031746031746","0","27632","0","0","3.24609375","13.046875","10","192","0","0" +"2","block","261","2026-09-29T02:05:34.3037607Z","261.0022931","0.99618559999999","11.71875","0.196060352608994","27632","0.015625","0.196060352608994","3.24609375","13.046875","10","192","0","0" +"2","block","262","2026-09-29T02:05:35.3049897Z","262.0036294","1.00133629999999","6.640625","0","27632","0","0","3.24609375","13.046875","10","192","0","0" +"2","block","263","2026-09-29T02:05:36.3064406Z","263.0053349","1.00170550000001","9.5703125","0.19497996167536","27632","0","0","3.24609375","13.046875","10","192","0","0" +"2","block","264","2026-09-29T02:05:37.3103739Z","264.0089553","1.00362039999999","10.1923076923077","0.194607941408925","27632","0","0","3.24609375","13.046875","10","192","0","0" +"2","block","265","2026-09-29T02:05:38.3097726Z","265.008401","0.999445700000024","10.546875","0","27632","0","0","3.24609375","13.046875","10","192","0","0" +"2","block","266","2026-09-29T02:05:39.3110061Z","266.009521","1.00112000000001","5.46875","0","27632","0","0","3.24609375","13.046875","10","192","0","0" +"2","block","267","2026-09-29T02:05:40.3155493Z","267.0143292","1.00480819999996","4.8828125","0","27632","0","0","3.26953125","13.21875","12","194","0","0" +"2","block","268","2026-09-29T02:05:41.3037863Z","268.0026359","0.98830670000001","8.33333333333333","0","27632","0","0","3.26953125","13.21875","12","194","0","0" +"2","block","269","2026-09-29T02:05:42.3037430Z","269.0023067","0.99967079999999","7.2265625","0","27632","0","0","3.26953125","13.21875","12","194","0","0" +"2","block","270","2026-09-29T02:05:43.3052348Z","270.003794","1.00148730000001","9.1796875","0","27632","0","0","3.26953125","13.21875","12","194","0","0" +"2","block","271","2026-09-29T02:05:44.3055143Z","271.0040511","1.0002571","10.15625","0.195262298063168","27632","0","0","3.27734375","13.22265625","12","194","0","0" +"2","block","272","2026-09-29T02:05:45.3169981Z","272.0155069","1.01145580000002","7.69230769230769","0","27632","0","0","3.27734375","13.2265625","12","194","0","0" +"2","block","273","2026-09-29T02:05:46.3065814Z","273.0052673","0.989760400000023","17.0634920634921","0","27632","0","0","3.27734375","13.2265625","12","194","0","0" +"2","block","274","2026-09-29T02:05:47.3051737Z","274.0038107","0.99854339999996","7.421875","0.195597407183311","27632","0","0","3.27734375","13.2265625","12","194","0","0" +"2","block","275","2026-09-29T02:05:48.3149577Z","275.0137581","1.00994739999999","6.15384615384615","0.193388784406002","27632","0","0","3.27734375","13.2265625","12","194","0","0" +"2","block","276","2026-09-29T02:05:49.3150009Z","276.0135618","0.999803700000029","7.6171875","0","27632","0","0","3.27734375","13.2265625","12","194","0","0" +"2","block","277","2026-09-29T02:05:50.3025033Z","277.0010722","0.987510400000019","8.73015873015873","0","27632","0","0","3.27734375","13.2265625","12","194","0","0" +"2","block","278","2026-09-29T02:05:51.3151309Z","278.0136775","1.01260529999996","6.53846153846154","0","27632","0","0","3.27734375","13.2265625","12","194","0","0" +"2","block","279","2026-09-29T02:05:52.3161999Z","279.0148565","1.00117900000004","5.859375","0","27632","0","0","3.27734375","13.2265625","12","194","0","0" +"2","block","280","2026-09-29T02:05:53.3155366Z","280.0137744","0.998917899999981","10.15625","0","27632","0","0","3.27734375","13.2265625","12","194","0","0" +"2","block","281","2026-09-29T02:05:54.3145693Z","281.0131824","0.999408000000017","5.078125","0","27632","0","0","3.27734375","13.2265625","12","194","0","0" +"2","block","282","2026-09-29T02:05:55.3155544Z","282.0141413","1.0009589","4.8828125","0","27632","0","0","3.2890625","13.23046875","12","194","0","0" +"2","block","283","2026-09-29T02:05:56.3179038Z","283.0164894","1.00234810000001","6.8359375","0","27632","0","0","3.2890625","13.23046875","12","194","0","0" +"2","block","284","2026-09-29T02:05:57.3037426Z","284.0022728","0.985783400000003","8.15109343936382","0","27632","0","0","3.2890625","13.23046875","12","194","0","0" +"2","block","285","2026-09-29T02:05:58.3187905Z","285.0174268","1.015154","7.10172744721689","0","27632","0","0","3.2890625","13.23046875","12","194","0","0" +"2","block","286","2026-09-29T02:05:59.3053524Z","286.0040359","0.986609099999953","5.75396825396825","0","27632","0","0","3.2890625","13.23046875","12","194","0","0" +"2","block","287","2026-09-29T02:06:00.3066100Z","287.0051783","1.00114240000005","9.1796875","0.195089629607127","27632","0","0","3.2890625","13.23046875","12","194","0","0" +"2","block","288","2026-09-29T02:06:01.3081637Z","288.0067134","1.00153509999996","15","0.195013135335954","27632","0","0","3.2890625","13.23046875","12","194","0","0" +"2","block","289","2026-09-29T02:06:02.3108145Z","289.0093366","1.00262320000002","6.25","0","27632","0","0","3.2890625","13.23046875","12","194","0","0" +"2","block","290","2026-09-29T02:06:03.3128610Z","290.0116333","1.00229669999999","7.03125","0","27632","0","0","3.2890625","13.23046875","12","194","0","0" +"2","block","291","2026-09-29T02:06:04.3133905Z","291.0120514","1.00041809999999","5.078125","0","27632","0","0","3.2890625","13.23046875","12","194","0","0" +"2","block","292","2026-09-29T02:06:05.3177955Z","292.0160527","1.00400130000003","7.2265625","0","27632","0","0","3.2890625","13.234375","12","194","0","0" +"2","block","293","2026-09-29T02:06:06.3158559Z","293.0144622","0.99840949999998","7.421875","0","27632","0","0","3.2890625","13.23828125","12","194","0","0" +"2","block","294","2026-09-29T02:06:07.3024085Z","294.0009236","0.986461399999996","3.96825396825397","0","27632","0","0","3.2890625","13.23828125","12","194","0","0" +"2","block","295","2026-09-29T02:06:08.3079170Z","295.0064778","1.00555420000001","6.56370656370656","0.194233687254251","27632","0.015625","0.194233687254251","3.2890625","13.23828125","12","194","0","0" +"2","block","296","2026-09-29T02:06:09.3058275Z","296.0044289","0.997951100000023","13.2411067193676","0","27632","0","0","3.2890625","13.23828125","12","194","0","0" +"2","block","297","2026-09-29T02:06:10.3038113Z","297.0023705","0.99794159999999","8.203125","0.195715360498051","27632","0","0","3.2890625","13.23828125","12","194","0","0" +"2","block","298","2026-09-29T02:06:11.3075218Z","298.0061142","1.00374370000003","10.3515625","0","27632","0","0","3.2890625","13.23828125","12","194","0","0" +"2","block","299","2026-09-29T02:06:12.3061429Z","299.0047868","0.998672599999963","13.0859375","0","27632","0","0","3.2890625","13.23828125","12","194","0","0" +"2","block","300","2026-09-29T02:06:13.3032919Z","300.001951","0.997164199999986","12.6953125","0.195867942310808","27632","0","0","3.2890625","13.23828125","12","194","0","0" diff --git a/docs/benchmarks/2026-09-29-v0.2.0/results/run-3-allow-end.json b/docs/benchmarks/2026-09-29-v0.2.0/results/run-3-allow-end.json new file mode 100644 index 0000000..14f27af --- /dev/null +++ b/docs/benchmarks/2026-09-29-v0.2.0/results/run-3-allow-end.json @@ -0,0 +1,12 @@ +{ + "state": "allow", + "layers": { + "shutdown": false, + "local_shutdown": false, + "update": false, + "remote": false, + "sleep": false + }, + "uptime_seconds": 333, + "blocked_count": 0 +} diff --git a/docs/benchmarks/2026-09-29-v0.2.0/results/run-3-allow-start.json b/docs/benchmarks/2026-09-29-v0.2.0/results/run-3-allow-start.json new file mode 100644 index 0000000..d41852d --- /dev/null +++ b/docs/benchmarks/2026-09-29-v0.2.0/results/run-3-allow-start.json @@ -0,0 +1,12 @@ +{ + "state": "allow", + "layers": { + "shutdown": false, + "local_shutdown": false, + "update": false, + "remote": false, + "sleep": false + }, + "uptime_seconds": 32, + "blocked_count": 0 +} diff --git a/docs/benchmarks/2026-09-29-v0.2.0/results/run-3-allow.csv b/docs/benchmarks/2026-09-29-v0.2.0/results/run-3-allow.csv new file mode 100644 index 0000000..eb5ddd0 --- /dev/null +++ b/docs/benchmarks/2026-09-29-v0.2.0/results/run-3-allow.csv @@ -0,0 +1,301 @@ +"run","scenario","sample","utc","elapsed_s","interval_s","system_cpu_pct","collector_cpu_pct","pid","process_cpu_s","cpu_pct","private_mib","working_set_mib","threads","handles","io_read_bytes_s","io_write_bytes_s" +"3","allow","1","2026-09-29T02:23:20.9401040Z","1.0063695","1.0063666","6.42023346303502","0","25272","0","0","1.99609375","12.578125","8","176","0","0" +"3","allow","2","2026-09-29T02:23:21.9593436Z","2.0255063","1.0191368","6.55105973025048","0","25272","0","0","1.99609375","12.578125","8","176","0","0" +"3","allow","3","2026-09-29T02:23:22.9477151Z","3.013864","0.9883577","9.30693069306931","0","25272","0","0","1.99609375","12.578125","8","176","0","0" +"3","allow","4","2026-09-29T02:23:23.9342896Z","4.0005035","0.9866395","10.7421875","0.197957308621842","25272","0","0","1.99609375","12.578125","8","176","0","0" +"3","allow","5","2026-09-29T02:23:24.9372776Z","5.003565","1.0030615","5.859375","0","25272","0","0","1.99609375","12.578125","8","176","0","0" +"3","allow","6","2026-09-29T02:23:25.9388729Z","6.0050737","1.0015087","9.9609375","0","25272","0","0","1.99609375","12.578125","8","176","0","0" +"3","allow","7","2026-09-29T02:23:26.9457535Z","7.0120368","1.0069631","7.03125","0","25272","0","0","1.99609375","12.578125","8","176","0","0" +"3","allow","8","2026-09-29T02:23:27.9463599Z","8.012482","1.0004452","6.0546875","0.195225585569304","25272","0","0","1.99609375","12.578125","8","176","0","0" +"3","allow","9","2026-09-29T02:23:28.9460369Z","9.0121983","0.999716299999999","6.0546875","0.195367925880572","25272","0","0","1.99609375","12.578125","8","176","0","0" +"3","allow","10","2026-09-29T02:23:29.9430703Z","10.0093173","0.997119","5.46875","0.195876821121652","25272","0","0","1.99609375","12.578125","8","176","0","0" +"3","allow","11","2026-09-29T02:23:30.9444204Z","11.0106469","1.0013296","4.1015625","0","25272","0","0","1.99609375","12.578125","8","176","0","0" +"3","allow","12","2026-09-29T02:23:31.9477864Z","12.0139218","1.0032749","7.421875","0","25272","0","0","1.99609375","12.578125","8","176","0","0" +"3","allow","13","2026-09-29T02:23:32.9439252Z","13.0103044","0.996382599999999","6.4453125","0.196021588494219","25272","0","0","1.99609375","12.578125","8","176","0","0" +"3","allow","14","2026-09-29T02:23:33.9481101Z","14.0143341","1.0040297","7.03125","0","25272","0","0","1.99609375","12.578125","8","176","0","0" +"3","allow","15","2026-09-29T02:23:34.9445196Z","15.0108","0.9964659","12.3046875","0.196005201984333","25272","0","0","1.99609375","12.578125","8","176","0","0" +"3","allow","16","2026-09-29T02:23:35.9572477Z","16.0233726","1.0125726","8.46153846153846","0","25272","0","0","1.99609375","12.578125","8","176","0","0" +"3","allow","17","2026-09-29T02:23:36.9541196Z","17.020633","0.997260400000002","5.2734375","0","25272","0","0","1.99609375","12.578125","8","176","0","0" +"3","allow","18","2026-09-29T02:23:37.9434117Z","18.0095804","0.988947400000001","7.34126984126984","0","25272","0","0","1.99609375","12.578125","8","176","0","0" +"3","allow","19","2026-09-29T02:23:38.9446169Z","19.0108865","1.0013061","5.859375","0","25272","0","0","1.99609375","12.578125","8","176","0","0" +"3","allow","20","2026-09-29T02:23:39.9460784Z","20.0121523","1.0012658","6.640625","0","25272","0","0","1.99609375","12.578125","8","176","0","0" +"3","allow","21","2026-09-29T02:23:40.9462259Z","21.0124788","1.0003265","7.6171875","0.195248751282706","25272","0","0","1.99609375","12.578125","8","176","0","0" +"3","allow","22","2026-09-29T02:23:41.9450893Z","22.0113567","0.9988779","13.28125","0.195531906352118","25272","0","0","1.99609375","12.578125","8","176","0","0" +"3","allow","23","2026-09-29T02:23:42.9455986Z","23.0120194","1.0006627","5.6640625","0.195183152125087","25272","0","0","1.99609375","12.578125","8","176","0","0" +"3","allow","24","2026-09-29T02:23:43.9440744Z","24.0104759","0.9984565","5.46875","0.195614430874054","25272","0","0","1.99609375","12.578125","8","176","0","0" +"3","allow","25","2026-09-29T02:23:44.9503367Z","25.0172367","1.0067608","10.1923076923077","0","25272","0","0","1.99609375","12.578125","8","176","0","0" +"3","allow","26","2026-09-29T02:23:45.9348913Z","26.0005933","0.9833566","9.32539682539683","0","25272","0","0","1.99609375","12.578125","8","176","0","0" +"3","allow","27","2026-09-29T02:23:46.9494873Z","27.0157492","1.0151559","7.5","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","28","2026-09-29T02:23:47.9485906Z","28.0147357","0.998986500000001","9.375","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","29","2026-09-29T02:23:48.9392060Z","29.0053443","0.990608599999998","10.9126984126984","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","30","2026-09-29T02:23:49.9376777Z","30.0038739","0.998529600000001","7.24070450097847","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","31","2026-09-29T02:23:50.9433342Z","31.0095884","1.0057145","6.23781676413255","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","32","2026-09-29T02:23:51.9575575Z","32.0237292","1.0141408","6.15384615384615","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","33","2026-09-29T02:23:52.9564168Z","33.0225577","0.998828500000002","9.39334637964775","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","34","2026-09-29T02:23:53.9393250Z","34.0056609","0.983103199999995","5.94059405940594","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","35","2026-09-29T02:23:54.9573115Z","35.0235158","1.0178549","5.96153846153846","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","36","2026-09-29T02:23:55.9552646Z","36.0213604","0.997844600000001","9.19765166340509","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","37","2026-09-29T02:23:56.9372896Z","37.0037999","0.982439499999998","10.2970297029703","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","38","2026-09-29T02:23:57.9380361Z","38.0041962","1.0003963","7.421875","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","39","2026-09-29T02:23:58.9384479Z","39.004561","1.0003648","6.640625","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","40","2026-09-29T02:23:59.9352158Z","40.0013964","0.996835400000002","7.2265625","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","41","2026-09-29T02:24:00.9355973Z","41.0016372","1.0002408","7.03125","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","42","2026-09-29T02:24:01.9491582Z","42.0154495","1.0138123","8.26923076923077","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","43","2026-09-29T02:24:02.9455269Z","43.0118145","0.996365000000004","11.1111111111111","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","44","2026-09-29T02:24:03.9488797Z","44.0151923","1.0033778","7.11538461538461","0.194654994360051","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","45","2026-09-29T02:24:04.9355966Z","45.0017396","0.986547300000005","9.72222222222222","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","46","2026-09-29T02:24:05.9494750Z","46.0157346","1.01399499999999","7.88461538461539","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","47","2026-09-29T02:24:06.9343426Z","47.0004954","0.984760800000004","5.75396825396825","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","48","2026-09-29T02:24:07.9336243Z","47.9997633","0.9992679","13.8671875","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","49","2026-09-29T02:24:08.9340854Z","49.0002519","1.0004886","7.421875","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","50","2026-09-29T02:24:09.9476845Z","50.013863","1.01361110000001","16.40625","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","51","2026-09-29T02:24:10.9469075Z","51.0130922","0.999229199999995","33.7890625","0.195463163006046","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","52","2026-09-29T02:24:11.9335928Z","51.9997454","0.986653199999999","8.3984375","0.197954559920345","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","53","2026-09-29T02:24:12.9466937Z","53.0128554","1.01311","14.453125","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","54","2026-09-29T02:24:13.9476012Z","54.0139521","1.0010967","10.15625","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","55","2026-09-29T02:24:14.9340484Z","55.0003066","0.986354499999997","6.0546875","0.19801450695465","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","56","2026-09-29T02:24:15.9475728Z","56.0138027","1.0134961","13.28125","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","57","2026-09-29T02:24:16.9486457Z","57.0149793","1.0011766","13.4615384615385","0.390165930765861","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","58","2026-09-29T02:24:17.9454221Z","58.0116135","0.996634199999995","11.7063492063492","0.39194420580791","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","59","2026-09-29T02:24:18.9444642Z","59.0106448","0.999031299999999","5.6640625","0.195501882673746","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","60","2026-09-29T02:24:19.9444567Z","60.0106016","0.9999568","6.4453125","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","61","2026-09-29T02:24:20.9530649Z","61.0186187","1.0080171","10.1923076923077","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","62","2026-09-29T02:24:21.9376574Z","62.0034105","0.984791799999996","6.74603174603175","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","63","2026-09-29T02:24:22.9373338Z","63.0030207","0.999610200000006","4.6875","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","64","2026-09-29T02:24:23.9357944Z","64.0019349","0.998914199999994","5.859375","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","65","2026-09-29T02:24:24.9482885Z","65.0145512","1.0126163","6.25","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","66","2026-09-29T02:24:25.9362392Z","66.001852","0.9873008","6.25","0.197824715628712","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","67","2026-09-29T02:24:26.9383405Z","67.0044956","1.0026436","8.0078125","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","68","2026-09-29T02:24:27.9385689Z","68.0043395","0.999843900000002","6.0546875","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","69","2026-09-29T02:24:28.9380259Z","69.0042561","0.999916599999992","7.6171875","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","70","2026-09-29T02:24:29.9348577Z","70.0011081","0.996852000000004","9.1796875","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","71","2026-09-29T02:24:30.9349823Z","71.0011346","1.0000265","5.859375","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","72","2026-09-29T02:24:31.9375722Z","72.003933","1.0027984","8.3984375","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","73","2026-09-29T02:24:32.9423710Z","73.0081137","1.00418069999999","5.2734375","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","74","2026-09-29T02:24:33.9557832Z","74.0218494","1.0137357","7.5","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","75","2026-09-29T02:24:34.9370368Z","75.0032243","0.981374900000006","3.96825396825397","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","76","2026-09-29T02:24:35.9381463Z","76.0043379","1.0011136","7.03125","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","77","2026-09-29T02:24:36.9404592Z","77.0060903","1.0017524","8.203125","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","78","2026-09-29T02:24:37.9582799Z","78.0244345","1.0183442","5.57692307692308","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","79","2026-09-29T02:24:38.9569893Z","79.0233053","0.998870799999992","7.03125","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","80","2026-09-29T02:24:39.9463939Z","80.0126135","0.989308200000011","4.56349206349206","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","81","2026-09-29T02:24:40.9456121Z","81.011961","0.999347499999999","8.0078125","0.195440024616062","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","82","2026-09-29T02:24:41.9458119Z","82.0120007","1.0000397","7.8125","0.195304746401567","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","83","2026-09-29T02:24:42.9462649Z","83.0125197","1.000519","4.8828125","0.195211185394781","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","84","2026-09-29T02:24:43.9489102Z","84.0151445","1.00262479999999","13.8461538461538","0.194801185847389","25272","0.015625","0.194801185847389","1.96484375","12.5625","7","176","0","0" +"3","allow","85","2026-09-29T02:24:44.9476162Z","85.013747","0.998602500000004","16.2698412698413","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","86","2026-09-29T02:24:45.9463436Z","86.012559","0.998812000000001","12.890625","0.19554480723099","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","87","2026-09-29T02:24:46.9492080Z","87.0153699","1.0028109","15.5769230769231","0.194765034963222","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","88","2026-09-29T02:24:47.9374523Z","88.0035859","0.988215999999994","9.72222222222222","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","89","2026-09-29T02:24:48.9390799Z","89.0051849","1.001599","5.078125","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","90","2026-09-29T02:24:49.9388764Z","90.0050046","0.999819700000003","6.4453125","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","91","2026-09-29T02:24:50.9446345Z","91.0107648","1.0057602","5.6640625","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","92","2026-09-29T02:24:51.9461930Z","92.0124034","1.00163860000001","7.03125","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","93","2026-09-29T02:24:52.9353372Z","93.0014192","0.989015800000004","9.9609375","0.197481678250235","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","94","2026-09-29T02:24:53.9381590Z","94.0042795","1.00286029999999","5.46875","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","95","2026-09-29T02:24:54.9405005Z","95.0067718","1.0024923","5.46875","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","96","2026-09-29T02:24:55.9575683Z","96.0236787","1.0169069","5.38461538461539","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","97","2026-09-29T02:24:56.9526602Z","97.0189802","0.995301500000011","17.96875","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","98","2026-09-29T02:24:57.9482230Z","98.0067255","0.9877453","7.93650793650794","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","99","2026-09-29T02:24:58.9427680Z","99.0088938","1.00216829999999","6.640625","0.194889920186062","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","100","2026-09-29T02:24:59.9464517Z","100.0133322","1.0044384","6.0546875","0","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","101","2026-09-29T02:25:00.9441449Z","101.010252","0.996919800000001","9.765625","0.195915960341042","25272","0","0","1.96484375","12.5625","7","176","0","0" +"3","allow","102","2026-09-29T02:25:01.9452356Z","102.011462","1.00121","7.2265625","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","103","2026-09-29T02:25:02.9465418Z","103.0128492","1.0013872","5.46875","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","104","2026-09-29T02:25:03.9462834Z","104.0124438","0.999594600000009","5.078125","0.195391711799962","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","105","2026-09-29T02:25:04.9453896Z","105.0115882","0.999144399999992","6.640625","0.19547975247622","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","106","2026-09-29T02:25:05.9459912Z","106.0123607","1.00077250000001","5.6640625","0.195161737557735","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","107","2026-09-29T02:25:06.9483171Z","107.0146306","1.00226989999999","16.1849710982659","0.389740328428505","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","108","2026-09-29T02:25:07.9373637Z","108.0034788","0.988848200000007","5.94059405940594","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","109","2026-09-29T02:25:08.9365829Z","109.0028176","0.999338800000004","5.2734375","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","110","2026-09-29T02:25:09.9475616Z","110.0137935","1.01097589999999","12.5","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","111","2026-09-29T02:25:10.9452368Z","111.0115062","0.997712700000008","9.1796875","0.195760262448296","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","112","2026-09-29T02:25:11.9581947Z","112.0243738","1.01286759999999","6.34615384615385","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","113","2026-09-29T02:25:12.9580951Z","113.0241818","0.999808000000002","6.4453125","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","114","2026-09-29T02:25:13.9573400Z","114.0233229","0.999141100000003","6.640625","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","115","2026-09-29T02:25:14.9564449Z","115.0225312","0.999208299999992","26.7578125","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","116","2026-09-29T02:25:15.9524794Z","116.0188382","0.996307000000002","22.0703125","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","117","2026-09-29T02:25:16.9453886Z","117.0115598","0.99272160000001","23.2142857142857","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","118","2026-09-29T02:25:17.9431316Z","118.0093146","0.997754799999996","7.8125","0.19575200239578","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","119","2026-09-29T02:25:18.9577731Z","119.0239372","1.0146226","8.26923076923077","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","120","2026-09-29T02:25:19.9584317Z","120.0246999","1.00076270000001","5.46875","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","121","2026-09-29T02:25:20.9554407Z","121.0215587","0.996858799999998","7.6320939334638","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","122","2026-09-29T02:25:21.9386866Z","122.004833","0.983274299999991","8.51485148514852","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","123","2026-09-29T02:25:22.9360190Z","123.0021438","0.997310800000008","5.2734375","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","124","2026-09-29T02:25:23.9388665Z","124.0049921","1.0028483","5.6640625","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","125","2026-09-29T02:25:24.9408726Z","125.0070751","1.002083","6.25","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","126","2026-09-29T02:25:25.9438970Z","126.0101255","1.00305040000001","8.3984375","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","127","2026-09-29T02:25:26.9581660Z","127.0244327","1.01430719999999","10.9615384615385","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","128","2026-09-29T02:25:27.9572828Z","128.022862","0.998429300000012","5.078125","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","129","2026-09-29T02:25:28.9580516Z","129.024317","1.00145499999999","6.8359375","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","130","2026-09-29T02:25:29.9539154Z","130.0201122","0.995795200000003","6.25","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","131","2026-09-29T02:25:30.9388991Z","131.0045322","0.98442","8.53174603174603","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","132","2026-09-29T02:25:31.9383825Z","132.0045058","0.999973600000004","5.46875","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","133","2026-09-29T02:25:32.9422068Z","133.0083839","1.00387809999998","6.640625","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","134","2026-09-29T02:25:33.9584375Z","134.0245859","1.01620200000002","7.11538461538461","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","135","2026-09-29T02:25:34.9496690Z","135.0159395","0.991353599999997","8.203125","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","136","2026-09-29T02:25:35.9390132Z","136.0051192","0.989179699999994","5.95238095238095","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","137","2026-09-29T02:25:36.9474915Z","137.01334","1.0082208","16.015625","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","138","2026-09-29T02:25:37.9464677Z","138.0126155","0.999275499999982","14.453125","0.195454106500163","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","139","2026-09-29T02:25:38.9461416Z","139.0122938","0.999678299999999","6.8359375","0.195375352250819","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","140","2026-09-29T02:25:39.9352022Z","140.0015019","0.989208100000013","8.0078125","0.197443288222162","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","141","2026-09-29T02:25:40.9368662Z","141.003013","1.00151109999999","4.8828125","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","142","2026-09-29T02:25:41.9353979Z","142.0018235","0.998810500000019","6.4453125","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","143","2026-09-29T02:25:42.9367922Z","143.0030318","1.0012083","4.6875","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","144","2026-09-29T02:25:43.9423516Z","144.0080848","1.005053","5.46875","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","145","2026-09-29T02:25:44.9450017Z","145.0108138","1.00272899999999","7.421875","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","146","2026-09-29T02:25:45.9464475Z","146.012651","1.00183720000001","12.3046875","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","147","2026-09-29T02:25:46.9472855Z","147.0136369","1.00098589999999","7.2265625","0.195120131062787","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","148","2026-09-29T02:25:47.9443190Z","148.010455","0.996818100000013","26.3671875","0.195935948594831","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","149","2026-09-29T02:25:48.9469924Z","149.0132401","1.00278509999998","7.6171875","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","150","2026-09-29T02:25:49.9448623Z","150.0110711","0.997830999999991","8.203125","0.195737053669411","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","151","2026-09-29T02:25:50.9455196Z","151.0116918","1.00062070000001","5.46875","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","152","2026-09-29T02:25:51.9441987Z","152.0103125","0.998620700000004","6.25","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","153","2026-09-29T02:25:52.9586758Z","153.0247695","1.01445699999999","6.92307692307692","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","154","2026-09-29T02:25:53.9581464Z","154.0243515","0.999582000000004","8.7890625","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","155","2026-09-29T02:25:54.9575278Z","155.0236718","0.999320299999994","3.90625","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","156","2026-09-29T02:25:55.9585187Z","156.0249318","1.00126","5.078125","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","157","2026-09-29T02:25:56.9573292Z","157.0234552","0.99852340000001","5.46875","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","158","2026-09-29T02:25:57.9564693Z","158.0225325","0.999077299999982","14.6484375","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","159","2026-09-29T02:25:58.9420322Z","159.0077475","0.985215000000011","5.35714285714286","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","160","2026-09-29T02:25:59.9556580Z","160.0219125","1.01416499999999","5.76923076923077","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","161","2026-09-29T02:26:00.9421311Z","161.0082365","0.986323999999996","6.94444444444444","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","162","2026-09-29T02:26:01.9384776Z","162.00464","0.996403500000014","7.6171875","0.196017476855508","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","163","2026-09-29T02:26:02.9373062Z","163.0034521","0.998812100000009","6.4453125","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","164","2026-09-29T02:26:03.9400925Z","164.0063654","1.00291329999999","4.6875","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","165","2026-09-29T02:26:04.9410617Z","165.007148","1.00078260000001","8.7890625","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","166","2026-09-29T02:26:05.9442589Z","166.0104976","1.00334959999998","6.0546875","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","167","2026-09-29T02:26:06.9473831Z","167.0135767","1.00307910000001","7.03125","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","168","2026-09-29T02:26:07.9459778Z","168.0121242","0.998547500000001","12.6953125","0.195596604067408","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","169","2026-09-29T02:26:08.9399526Z","169.0061022","0.993977999999998","8.984375","0.196495797693712","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","170","2026-09-29T02:26:09.9403367Z","170.0066677","1.00056549999999","8.0078125","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","171","2026-09-29T02:26:10.9451562Z","171.0112953","1.00462760000002","11.1328125","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","172","2026-09-29T02:26:11.9435489Z","172.009659","0.998363699999999","5.2734375","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","173","2026-09-29T02:26:12.9455894Z","173.0118685","1.00220949999999","5.2734375","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","174","2026-09-29T02:26:13.9484978Z","174.0146744","1.0028059","17.0873786407767","0.194766006063586","25272","0.015625","0.194766006063586","1.90625","12.5390625","5","174","0","0" +"3","allow","175","2026-09-29T02:26:14.9482325Z","175.0143448","0.999670400000014","10.4125736738703","0.195376896224993","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","176","2026-09-29T02:26:15.9468489Z","176.0130281","0.998683299999982","13.8671875","0.195570007028258","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","177","2026-09-29T02:26:16.9478539Z","177.014069","1.00104089999999","9.765625","0.195109410614493","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","178","2026-09-29T02:26:17.9444029Z","178.0105256","0.996456600000016","13.28125","0.196007031314758","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","179","2026-09-29T02:26:18.9342944Z","179.0004246","0.989899000000008","9.375","0.197305482680555","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","180","2026-09-29T02:26:19.9340583Z","180.0002045","0.999779899999993","6.8359375","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","181","2026-09-29T02:26:20.9389466Z","181.0050802","1.00487569999999","6.8359375","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","182","2026-09-29T02:26:21.9424373Z","182.0086046","1.0035244","8.984375","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","183","2026-09-29T02:26:22.9441304Z","183.0103104","1.0017058","6.8359375","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","184","2026-09-29T02:26:23.9575102Z","184.0236569","1.01334650000001","5.19230769230769","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","185","2026-09-29T02:26:24.9555737Z","185.0216943","0.998037399999987","7.8125","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","186","2026-09-29T02:26:25.9420336Z","186.0081213","0.98642700000002","6.15079365079365","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","187","2026-09-29T02:26:26.9472929Z","187.013621","1.0054997","5.078125","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","188","2026-09-29T02:26:27.9473296Z","188.0135669","0.9999459","9.9609375","0.195323066977923","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","189","2026-09-29T02:26:28.9350892Z","189.0012275","0.987660599999998","8.203125","0.197752649037534","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","190","2026-09-29T02:26:29.9392939Z","190.005436","1.0042085","7.2265625","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","191","2026-09-29T02:26:30.9356899Z","191.0017525","0.996316499999978","8.0078125","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","192","2026-09-29T02:26:31.9382976Z","192.0044919","1.00273940000002","5.859375","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","193","2026-09-29T02:26:32.9422072Z","193.0083142","1.0038223","6.25","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","194","2026-09-29T02:26:33.9567898Z","194.02306","1.01474579999999","6.92307692307692","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","195","2026-09-29T02:26:34.9557527Z","195.0220703","0.999010300000009","10.15625","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","196","2026-09-29T02:26:35.9591169Z","196.0252448","1.0031745","5.2734375","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","197","2026-09-29T02:26:36.9459417Z","197.0123765","0.987131699999992","5.75396825396825","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","198","2026-09-29T02:26:37.9427447Z","198.0089692","0.996592700000008","9.765625","0.195980263551999","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","199","2026-09-29T02:26:38.9566534Z","199.0229008","1.01393160000001","5","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","200","2026-09-29T02:26:39.9397049Z","200.0058376","0.982936799999976","5.15873015873016","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","201","2026-09-29T02:26:40.9402454Z","201.0064597","1.00062210000002","5.46875","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","202","2026-09-29T02:26:41.9548201Z","202.0209248","1.0144651","5.96153846153846","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","203","2026-09-29T02:26:42.9428026Z","203.0090824","0.988157599999994","4.76190476190476","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","204","2026-09-29T02:26:43.9483560Z","204.0146391","1.0055567","9.92217898832685","0.19423320435337","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","205","2026-09-29T02:26:44.9407385Z","205.0070343","0.992395200000004","10.7843137254902","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","206","2026-09-29T02:26:45.9564621Z","206.022626","1.01559170000002","7.5","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","207","2026-09-29T02:26:46.9417539Z","207.007957","0.985331000000002","5.55555555555556","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","208","2026-09-29T02:26:47.9533440Z","208.0194695","1.01151249999998","9.42307692307692","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","209","2026-09-29T02:26:48.9411617Z","209.0074219","0.987952400000012","8.13492063492063","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","210","2026-09-29T02:26:49.9447934Z","210.0110193","1.00359739999999","6.8359375","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","211","2026-09-29T02:26:50.9460137Z","211.0122","1.00118069999999","11.5234375","0.195082166486032","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","212","2026-09-29T02:26:51.9392196Z","212.0053571","0.993157100000019","6.0546875","0.196658212482191","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","213","2026-09-29T02:26:52.9401917Z","213.0064344","1.00107729999999","5.859375","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","214","2026-09-29T02:26:53.9562353Z","214.0220115","1.0155771","7.5","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","215","2026-09-29T02:26:54.9523046Z","215.0184898","0.996478300000007","4.6875","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","216","2026-09-29T02:26:55.9483874Z","216.0145999","0.996110099999981","14.0316205533597","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","217","2026-09-29T02:26:56.9470213Z","217.0131283","0.998528399999998","13.3333333333333","0.195600345468392","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","218","2026-09-29T02:26:57.9349388Z","218.001006","0.987877700000013","13.8671875","0.197709190115333","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","219","2026-09-29T02:26:58.9343620Z","219.0005194","0.999513400000012","15.625","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","220","2026-09-29T02:26:59.9472000Z","220.0133857","1.01286629999998","11.71875","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","221","2026-09-29T02:27:00.9495047Z","221.0156612","1.0022755","7.30769230769231","0.194869075418885","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","222","2026-09-29T02:27:01.9480155Z","222.0141511","0.99848990000001","9.92063492063492","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","223","2026-09-29T02:27:02.9352606Z","223.0013479","0.987196799999992","11.328125","0.197845556225467","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","224","2026-09-29T02:27:03.9433778Z","224.0095165","1.0081686","6.640625","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","225","2026-09-29T02:27:04.9488739Z","225.01516","1.00564349999999","8.46153846153846","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","226","2026-09-29T02:27:05.9460439Z","226.0122917","0.997131700000011","12.5","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","227","2026-09-29T02:27:06.9428029Z","227.0089017","0.996610000000004","5.859375","0.19597686156069","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","228","2026-09-29T02:27:07.9451878Z","228.0113007","1.002399","9.5703125","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","229","2026-09-29T02:27:08.9467977Z","229.0129775","1.00167679999998","8.7890625","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","230","2026-09-29T02:27:09.9333127Z","229.9996613","0.986683800000009","12.5","0.19794842076053","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","231","2026-09-29T02:27:10.9350673Z","231.0011145","1.00145320000001","5.6640625","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","232","2026-09-29T02:27:11.9499689Z","232.0160627","1.01494819999999","6.34615384615385","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","233","2026-09-29T02:27:12.9471248Z","233.0133114","0.9972487","16.468253968254","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","234","2026-09-29T02:27:13.9368116Z","234.0028611","0.989549699999998","37.109375","0.197375129313869","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","235","2026-09-29T02:27:14.9428418Z","235.009029","1.00616790000001","19.140625","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","236","2026-09-29T02:27:15.9554395Z","236.0216291","1.01260009999999","8.46153846153846","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","237","2026-09-29T02:27:16.9372955Z","237.0033947","0.981765600000017","7.93650793650794","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","238","2026-09-29T02:27:17.9391722Z","238.005393","1.0019983","6.8359375","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","239","2026-09-29T02:27:18.9391659Z","239.0053686","0.999975599999999","8.59375","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","240","2026-09-29T02:27:19.9400276Z","240.0061204","1.00075179999999","4.30528375733855","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","241","2026-09-29T02:27:20.9390300Z","241.0052424","0.999122","8.38206627680312","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","242","2026-09-29T02:27:21.9370013Z","242.003169","0.9979266","6.8359375","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","243","2026-09-29T02:27:22.9366150Z","243.0028212","0.999652200000014","4.1015625","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","244","2026-09-29T02:27:23.9370728Z","244.0034329","1.00061169999998","5.859375","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","245","2026-09-29T02:27:24.9481842Z","245.0139278","1.0104949","8.0078125","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","246","2026-09-29T02:27:25.9384179Z","246.0046635","0.990735700000016","8.7890625","0.197138853480294","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","247","2026-09-29T02:27:26.9385219Z","247.0048357","1.00017220000001","4.8828125","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","248","2026-09-29T02:27:27.9359199Z","248.0020278","0.997192099999978","5.859375","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","249","2026-09-29T02:27:28.9392224Z","249.0054229","1.00339510000001","8.0078125","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","250","2026-09-29T02:27:29.9432031Z","250.0094062","1.00398330000002","4.296875","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","251","2026-09-29T02:27:30.9475815Z","251.0138156","1.00440939999999","9.375","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","252","2026-09-29T02:27:31.9464389Z","252.012558","0.998742399999998","5.46875","0.19555843428696","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","253","2026-09-29T02:27:32.9353161Z","253.0014515","0.988893500000017","7.6171875","0.197506101516489","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","254","2026-09-29T02:27:33.9485060Z","254.0148208","1.01336929999999","10.2119460500963","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","255","2026-09-29T02:27:34.9402881Z","255.0064352","0.991614400000003","12.8712871287129","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","256","2026-09-29T02:27:35.9547701Z","256.0208114","1.01437620000002","7.30769230769231","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","257","2026-09-29T02:27:36.9445368Z","257.0107151","0.989903699999957","9.12698412698413","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","258","2026-09-29T02:27:37.9442682Z","258.010603","0.999887900000033","9.9609375","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","259","2026-09-29T02:27:38.9570746Z","259.0232252","1.01262220000001","8.65384615384615","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","260","2026-09-29T02:27:39.9581291Z","260.0243026","1.00107739999999","7.03125","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","261","2026-09-29T02:27:40.9502026Z","261.0163564","0.992053800000008","7.03125","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","262","2026-09-29T02:27:41.9491985Z","262.0153622","0.999005799999964","13.0859375","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","263","2026-09-29T02:27:42.9365315Z","263.002754","0.987391800000012","4.36507936507936","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","264","2026-09-29T02:27:43.9373071Z","264.0030938","1.00033980000001","5.2734375","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","265","2026-09-29T02:27:44.9465732Z","265.0127972","1.00970339999998","5.2734375","0","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","266","2026-09-29T02:27:45.9474025Z","266.013571","1.00077380000005","11.9140625","0.195161484043638","25272","0","0","1.90625","12.5390625","5","174","0","0" +"3","allow","267","2026-09-29T02:27:46.9464061Z","267.0126966","0.999125599999957","9.1796875","0.195483430711823","25272","0","0","1.96875","12.7109375","7","174","0","0" +"3","allow","268","2026-09-29T02:27:47.9470146Z","268.0132313","1.0005347","9.5703125","0.19520812221705","25272","0","0","1.96875","12.71484375","7","174","0","0" +"3","allow","269","2026-09-29T02:27:48.9338298Z","268.99989","0.986658700000021","9.5703125","0.197953456448512","25272","0","0","1.96875","12.71484375","7","174","0","0" +"3","allow","270","2026-09-29T02:27:49.9435466Z","270.009661","1.009771","5.2734375","0","25272","0","0","1.96875","12.71484375","7","174","0","0" +"3","allow","271","2026-09-29T02:27:50.9440556Z","271.0103293","1.00066829999997","6.0546875","0","25272","0","0","1.96875","12.71484375","7","174","0","0" +"3","allow","272","2026-09-29T02:27:51.9452094Z","272.0108805","1.00055120000002","5.078125","0","25272","0","0","1.96875","12.71484375","7","174","0","0" +"3","allow","273","2026-09-29T02:27:52.9495002Z","273.0158162","1.00493569999998","5.38461538461539","0","25272","0","0","1.96875","12.71484375","7","174","0","0" +"3","allow","274","2026-09-29T02:27:53.9464534Z","274.0120588","0.996242600000016","10.9126984126984","0","25272","0","0","1.96875","12.71484375","7","174","0","0" +"3","allow","275","2026-09-29T02:27:54.9351324Z","275.0012997","0.989240900000027","8.7890625","0.197436741647049","25272","0","0","1.96875","12.71484375","7","174","0","0" +"3","allow","276","2026-09-29T02:27:55.9380132Z","276.0046407","1.00334099999998","4.296875","0","25272","0","0","1.96875","12.71484375","7","174","0","0" +"3","allow","277","2026-09-29T02:27:56.9390456Z","277.0052669","1.0006262","6.25","0","25272","0","0","1.96875","12.71484375","7","174","0","0" +"3","allow","278","2026-09-29T02:27:57.9389034Z","278.0051019","0.999835000000019","7.6171875","0","25272","0","0","1.96875","12.71484375","7","174","0","0" +"3","allow","279","2026-09-29T02:27:58.9361128Z","279.0022068","0.997104900000011","12.890625","0","25272","0","0","1.96875","12.71484375","7","174","0","0" +"3","allow","280","2026-09-29T02:27:59.9469847Z","280.0131118","1.01090499999998","5.6640625","0","25272","0","0","1.96875","12.71484375","7","174","0","0" +"3","allow","281","2026-09-29T02:28:00.9338864Z","281.0001484","0.98703660000001","10.3515625","0.197877667352961","25272","0","0","1.96875","12.71484375","7","174","0","0" +"3","allow","282","2026-09-29T02:28:01.9473320Z","282.0135857","1.01343729999996","15.625","0","25272","0","0","1.96875","12.71484375","7","174","0","0" +"3","allow","283","2026-09-29T02:28:02.9477471Z","283.0140175","1.0004318","10.15625","0.19522820046304","25272","0","0","1.96875","12.71484375","7","174","0","0" +"3","allow","284","2026-09-29T02:28:03.9462409Z","284.012503","0.998485500000015","10.3515625","0.195608749451041","25272","0","0","1.96875","12.71484375","7","174","0","0" +"3","allow","285","2026-09-29T02:28:04.9467315Z","285.0130639","1.00056089999998","6.0546875","0.19520301063134","25272","0","0","1.96875","12.71484375","7","174","0","0" +"3","allow","286","2026-09-29T02:28:05.9418212Z","286.0079983","0.994934400000034","7.421875","0.196306912294914","25272","0","0","1.96875","12.71484375","7","174","0","0" +"3","allow","287","2026-09-29T02:28:06.9574547Z","287.0235915","1.01559320000001","7.69230769230769","0","25272","0","0","1.96875","12.71484375","7","174","0","0" +"3","allow","288","2026-09-29T02:28:07.9566658Z","288.0228539","0.999262399999964","5.859375","0","25272","0","0","1.96875","12.71484375","7","174","0","0" +"3","allow","289","2026-09-29T02:28:08.9354505Z","289.0015591","0.978705200000036","9.32539682539683","0","25272","0","0","1.96875","12.71484375","7","174","0","0" +"3","allow","290","2026-09-29T02:28:09.9402430Z","290.0065337","1.00497459999997","5.6640625","0","25272","0","0","1.96875","12.71484375","7","174","0","0" +"3","allow","291","2026-09-29T02:28:10.9516141Z","291.0114015","1.0048678","16.1793372319688","0","25272","0","0","1.96875","12.71484375","7","174","0","0" +"3","allow","292","2026-09-29T02:28:11.9411159Z","292.0073524","0.995950900000025","12.720156555773","0","25272","0","0","1.96875","12.71484375","7","174","0","0" +"3","allow","293","2026-09-29T02:28:12.9447966Z","293.0109466","1.00359420000001","8.3984375","0","25272","0","0","1.96875","12.71484375","7","174","0","0" +"3","allow","294","2026-09-29T02:28:13.9447302Z","294.0106116","0.999664999999993","9.5703125","0","25272","0","0","1.96875","12.71484375","7","174","0","0" +"3","allow","295","2026-09-29T02:28:14.9461556Z","295.0125066","1.00189499999999","6.4453125","0","25272","0","0","1.96875","12.71484375","7","174","0","0" +"3","allow","296","2026-09-29T02:28:15.9450298Z","296.0113646","0.998857999999984","9.375","0.195535801885757","25272","0","0","1.96875","12.71484375","7","174","0","0" +"3","allow","297","2026-09-29T02:28:16.9459212Z","297.0122074","1.00084279999999","7.2265625","0.195148029240958","25272","0","0","1.96875","12.71484375","7","174","0","0" +"3","allow","298","2026-09-29T02:28:17.9490198Z","298.0151957","1.00298830000003","11.1538461538462","0.194730586588094","25272","0","0","1.96875","12.71484375","7","174","0","0" +"3","allow","299","2026-09-29T02:28:18.9470072Z","299.013206","0.998010299999976","15.2777777777778","0","25272","0","0","1.96875","12.71484375","7","174","0","0" +"3","allow","300","2026-09-29T02:28:19.9374726Z","300.0035865","0.990380500000015","11.71875","0.197209557336798","25272","0","0","1.96875","12.71484375","7","174","0","0" diff --git a/docs/benchmarks/2026-09-29-v0.2.0/results/run-3-baseline.csv b/docs/benchmarks/2026-09-29-v0.2.0/results/run-3-baseline.csv new file mode 100644 index 0000000..bb57a1e --- /dev/null +++ b/docs/benchmarks/2026-09-29-v0.2.0/results/run-3-baseline.csv @@ -0,0 +1,301 @@ +"run","scenario","sample","utc","elapsed_s","interval_s","system_cpu_pct","collector_cpu_pct","pid","process_cpu_s","cpu_pct","private_mib","working_set_mib","threads","handles","io_read_bytes_s","io_write_bytes_s" +"3","baseline","1","2026-09-29T02:17:47.8680735Z","1.0102562","1.0102538","7.6171875","0","","","","","","","","","" +"3","baseline","2","2026-09-29T02:17:48.8710333Z","2.0131844","1.0029282","10.7692307692308","0","","","","","","","","","" +"3","baseline","3","2026-09-29T02:17:49.8730063Z","3.0150606","1.0018762","5.859375","0","","","","","","","","","" +"3","baseline","4","2026-09-29T02:17:50.8603783Z","4.0025087","0.9874481","6.54761904761905","0","","","","","","","","","" +"3","baseline","5","2026-09-29T02:17:51.8629434Z","5.005046","1.0025373","7.8125","0","","","","","","","","","" +"3","baseline","6","2026-09-29T02:17:52.8615484Z","6.0039621","0.9989161","20.1171875","0","","","","","","","","","" +"3","baseline","7","2026-09-29T02:17:53.8709673Z","7.0130116","1.0090495","7.88461538461539","0","","","","","","","","","" +"3","baseline","8","2026-09-29T02:17:54.8755365Z","8.017168","1.0041564","6.0546875","0","","","","","","","","","" +"3","baseline","9","2026-09-29T02:17:55.8612336Z","9.0033543","0.9861863","4.56349206349206","0","","","","","","","","","" +"3","baseline","10","2026-09-29T02:17:56.8596838Z","10.0017852","0.998430899999999","5.0880626223092","0","","","","","","","","","" +"3","baseline","11","2026-09-29T02:17:57.8589698Z","11.0010615","0.9992763","5.87084148727984","0","","","","","","","","","" +"3","baseline","12","2026-09-29T02:17:58.8583135Z","12.0003539","0.9992924","6.42023346303502","0","","","","","","","","","" +"3","baseline","13","2026-09-29T02:17:59.8635378Z","13.0050549","1.004701","5.859375","0","","","","","","","","","" +"3","baseline","14","2026-09-29T02:18:00.8614873Z","14.0035564","0.9985015","8.203125","0","","","","","","","","","" +"3","baseline","15","2026-09-29T02:18:01.8655951Z","15.0077155","1.0041591","16.40625","0","","","","","","","","","" +"3","baseline","16","2026-09-29T02:18:02.8680694Z","16.0101636","1.0024481","6.25","0","","","","","","","","","" +"3","baseline","17","2026-09-29T02:18:03.8669186Z","17.0090162","0.998852599999999","5.859375","0","","","","","","","","","" +"3","baseline","18","2026-09-29T02:18:04.8661922Z","18.0083503","0.999334100000002","4.8828125","0","","","","","","","","","" +"3","baseline","19","2026-09-29T02:18:05.8667773Z","19.008971","1.0006207","8.0078125","0","","","","","","","","","" +"3","baseline","20","2026-09-29T02:18:06.8780509Z","20.0201119","1.0111409","5.76923076923077","0","","","","","","","","","" +"3","baseline","21","2026-09-29T02:18:07.8661747Z","21.0086299","0.988517999999999","5.75396825396825","0","","","","","","","","","" +"3","baseline","22","2026-09-29T02:18:08.8682875Z","22.0105659","1.001936","6.0546875","0","","","","","","","","","" +"3","baseline","23","2026-09-29T02:18:09.8726793Z","23.01488","1.0043141","15.1923076923077","0","","","","","","","","","" +"3","baseline","24","2026-09-29T02:18:10.8702793Z","24.0124783","0.9975983","11.9140625","0.19578271133782","","","","","","","","","" +"3","baseline","25","2026-09-29T02:18:11.8684864Z","25.0105345","0.998056200000001","15.2777777777778","0","","","","","","","","","" +"3","baseline","26","2026-09-29T02:18:12.8667643Z","26.0088852","0.9983507","8.0078125","0","","","","","","","","","" +"3","baseline","27","2026-09-29T02:18:13.8662323Z","27.0083483","0.9994631","10.546875","0","","","","","","","","","" +"3","baseline","28","2026-09-29T02:18:14.8674769Z","28.0095751","1.0012268","6.640625","0","","","","","","","","","" +"3","baseline","29","2026-09-29T02:18:15.8721613Z","29.01446","1.0048849","6.73076923076923","0","","","","","","","","","" +"3","baseline","30","2026-09-29T02:18:16.8709160Z","30.0129684","0.998508399999999","5.859375","0","","","","","","","","","" +"3","baseline","31","2026-09-29T02:18:17.8722786Z","31.0145901","1.0016217","5.6640625","0","","","","","","","","","" +"3","baseline","32","2026-09-29T02:18:18.8713203Z","32.0133835","0.998793399999997","5.078125","0","","","","","","","","","" +"3","baseline","33","2026-09-29T02:18:19.8675801Z","33.0097772","0.996393700000006","9.92063492063492","0","","","","","","","","","" +"3","baseline","34","2026-09-29T02:18:20.8678711Z","34.0101238","1.00034659999999","5.46875","0","","","","","","","","","" +"3","baseline","35","2026-09-29T02:18:21.8700821Z","35.0121049","1.0019811","15.8203125","0","","","","","","","","","" +"3","baseline","36","2026-09-29T02:18:22.8692002Z","36.0112227","0.9991178","7.8125","0.195484956828914","","","","","","","","","" +"3","baseline","37","2026-09-29T02:18:23.8708421Z","37.013013","1.0017903","12.8846153846154","0","","","","","","","","","" +"3","baseline","38","2026-09-29T02:18:24.8767378Z","38.0188454","1.0058324","4.1015625","0","","","","","","","","","" +"3","baseline","39","2026-09-29T02:18:25.8638485Z","39.0059578","0.987112400000001","7.73809523809524","0","","","","","","","","","" +"3","baseline","40","2026-09-29T02:18:26.8638097Z","40.0065452","1.0005874","4.8828125","0","","","","","","","","","" +"3","baseline","41","2026-09-29T02:18:27.8629272Z","41.0051378","0.998592600000002","6.25","0","","","","","","","","","" +"3","baseline","42","2026-09-29T02:18:28.8659728Z","42.0082911","1.0031533","5.859375","0","","","","","","","","","" +"3","baseline","43","2026-09-29T02:18:29.8660544Z","43.0080787","0.999787599999998","5.46875","0","","","","","","","","","" +"3","baseline","44","2026-09-29T02:18:30.8676901Z","44.0098934","1.0018147","9.375","0","","","","","","","","","" +"3","baseline","45","2026-09-29T02:18:31.8662301Z","45.0083321","0.998438700000001","8.203125","0","","","","","","","","","" +"3","baseline","46","2026-09-29T02:18:32.8783310Z","46.0204955","1.0121634","11.9230769230769","0","","","","","","","","","" +"3","baseline","47","2026-09-29T02:18:33.8731981Z","47.0125218","0.992026299999999","13.8671875","0.196882381041712","","","","","","","","","" +"3","baseline","48","2026-09-29T02:18:34.8728723Z","48.0149896","1.00246780000001","20.5078125","0","","","","","","","","","" +"3","baseline","49","2026-09-29T02:18:35.8612610Z","49.0034623","0.988472699999996","5.36779324055666","0","","","","","","","","","" +"3","baseline","50","2026-09-29T02:18:36.8668836Z","50.0090408","1.00557850000001","5.26315789473684","0","","","","","","","","","" +"3","baseline","51","2026-09-29T02:18:37.8804104Z","51.0225232","1.01348239999999","6.92307692307692","0","","","","","","","","","" +"3","baseline","52","2026-09-29T02:18:38.8694860Z","52.0115461","0.989022900000002","6.34920634920635","0","","","","","","","","","" +"3","baseline","53","2026-09-29T02:18:39.8740889Z","53.0162553","1.0047092","6.73076923076923","0","","","","","","","","","" +"3","baseline","54","2026-09-29T02:18:40.8614703Z","54.0035301","0.987274800000002","7.14285714285714","0","","","","","","","","","" +"3","baseline","55","2026-09-29T02:18:41.8643691Z","55.0064234","1.0028933","9.375","0","","","","","","","","","" +"3","baseline","56","2026-09-29T02:18:42.8624113Z","56.0047373","0.998313899999999","10.7421875","0","","","","","","","","","" +"3","baseline","57","2026-09-29T02:18:43.8724211Z","57.0147115","1.0099742","7.5","0","","","","","","","","","" +"3","baseline","58","2026-09-29T02:18:44.8694637Z","58.0116176","0.996906099999997","15.2777777777778","0","","","","","","","","","" +"3","baseline","59","2026-09-29T02:18:45.8687676Z","59.0109753","0.999357700000004","16.015625","0","","","","","","","","","" +"3","baseline","60","2026-09-29T02:18:46.8714713Z","60.0136108","1.0026355","14.6153846153846","0","","","","","","","","","" +"3","baseline","61","2026-09-29T02:18:47.8763655Z","61.0184298","1.004819","5.859375","0","","","","","","","","","" +"3","baseline","62","2026-09-29T02:18:48.8725637Z","62.0146968","0.996266999999996","5.46875","0","","","","","","","","","" +"3","baseline","63","2026-09-29T02:18:49.8718934Z","63.0140784","0.9993816","10.3515625","0","","","","","","","","","" +"3","baseline","64","2026-09-29T02:18:50.8697496Z","64.0118818","0.997803400000002","9.72222222222222","0","","","","","","","","","" +"3","baseline","65","2026-09-29T02:18:51.8688017Z","65.0109219","0.999040100000002","15.8203125","0.195500160604164","","","","","","","","","" +"3","baseline","66","2026-09-29T02:18:52.8654882Z","66.0076504","0.996728500000003","24.609375","0","","","","","","","","","" +"3","baseline","67","2026-09-29T02:18:53.8718104Z","67.0138807","1.0062303","43.6538461538462","0","","","","","","","","","" +"3","baseline","68","2026-09-29T02:18:54.8763167Z","68.0184954","1.00461469999999","15.0390625","0","","","","","","","","","" +"3","baseline","69","2026-09-29T02:18:55.8596462Z","69.0016809","0.983185500000005","4.96031746031746","0","","","","","","","","","" +"3","baseline","70","2026-09-29T02:18:56.8620510Z","70.0041441","1.00246319999999","4.8828125","0","","","","","","","","","" +"3","baseline","71","2026-09-29T02:18:57.8649364Z","71.0069847","1.00284060000001","9.9609375","0","","","","","","","","","" +"3","baseline","72","2026-09-29T02:18:58.8637783Z","72.0059744","0.998989699999996","7.2265625","0","","","","","","","","","" +"3","baseline","73","2026-09-29T02:18:59.8619443Z","73.0042195","0.998245099999991","6.25","0","","","","","","","","","" +"3","baseline","74","2026-09-29T02:19:00.8607503Z","74.0029379","0.998718400000001","7.03125","0","","","","","","","","","" +"3","baseline","75","2026-09-29T02:19:01.8621488Z","75.0041535","1.00121560000001","8.984375","0","","","","","","","","","" +"3","baseline","76","2026-09-29T02:19:02.8628879Z","76.0051374","1.00098389999999","8.7890625","0","","","","","","","","","" +"3","baseline","77","2026-09-29T02:19:03.8614118Z","77.0035895","0.998452099999994","7.2265625","0","","","","","","","","","" +"3","baseline","78","2026-09-29T02:19:04.8672642Z","78.0094905","1.00590100000001","7.8125","0","","","","","","","","","" +"3","baseline","79","2026-09-29T02:19:05.8717836Z","79.0137396","1.0042491","10.5769230769231","0","","","","","","","","","" +"3","baseline","80","2026-09-29T02:19:06.8735849Z","80.0157309","1.0019913","4.8828125","0","","","","","","","","","" +"3","baseline","81","2026-09-29T02:19:07.8724871Z","81.0145772","0.998846299999997","4.6875","0","","","","","","","","","" +"3","baseline","82","2026-09-29T02:19:08.8583442Z","82.0003958","0.985818600000002","7.14285714285714","0","","","","","","","","","" +"3","baseline","83","2026-09-29T02:19:09.8624322Z","83.0045239","1.0041281","13.8671875","0","","","","","","","","","" +"3","baseline","84","2026-09-29T02:19:10.8622971Z","84.0044787","0.999954799999998","6.0546875","0","","","","","","","","","" +"3","baseline","85","2026-09-29T02:19:11.8616614Z","85.0038642","0.999385500000002","7.8125","0","","","","","","","","","" +"3","baseline","86","2026-09-29T02:19:12.8645742Z","86.0066488","1.0027846","8.984375","0","","","","","","","","","" +"3","baseline","87","2026-09-29T02:19:13.8646911Z","87.0063674","0.999718600000008","10.3515625","0","","","","","","","","","" +"3","baseline","88","2026-09-29T02:19:14.8681324Z","88.0104051","1.0040377","6.25","0","","","","","","","","","" +"3","baseline","89","2026-09-29T02:19:15.8701548Z","89.0122467","1.00184159999999","8.7890625","0","","","","","","","","","" +"3","baseline","90","2026-09-29T02:19:16.8685438Z","90.0107649","0.998518200000007","11.1328125","0.195602343552675","","","","","","","","","" +"3","baseline","91","2026-09-29T02:19:17.8652679Z","91.0074411","0.996676199999996","8.59375","0","","","","","","","","","" +"3","baseline","92","2026-09-29T02:19:18.8743219Z","92.0164819","1.00904080000001","10.5769230769231","0","","","","","","","","","" +"3","baseline","93","2026-09-29T02:19:19.8718899Z","93.0139949","0.997512999999998","18.9453125","0","","","","","","","","","" +"3","baseline","94","2026-09-29T02:19:20.8711407Z","94.0132905","0.999295599999996","23.2421875","0","","","","","","","","","" +"3","baseline","95","2026-09-29T02:19:21.8711727Z","95.0132891","0.999998599999998","16.015625","0","","","","","","","","","" +"3","baseline","96","2026-09-29T02:19:22.8705642Z","96.0127215","0.999432400000003","8.7890625","0","","","","","","","","","" +"3","baseline","97","2026-09-29T02:19:23.8724767Z","97.0145091","1.0017876","8.02348336594912","0","","","","","","","","","" +"3","baseline","98","2026-09-29T02:19:24.8609056Z","98.0029672","0.988458100000003","5.34653465346535","0","","","","","","","","","" +"3","baseline","99","2026-09-29T02:19:25.8634094Z","99.0054534","1.00248619999999","6.25","0","","","","","","","","","" +"3","baseline","100","2026-09-29T02:19:26.8617707Z","100.0039272","0.998473799999999","6.4453125","0","","","","","","","","","" +"3","baseline","101","2026-09-29T02:19:27.8662791Z","101.008379","1.0044518","5.859375","0","","","","","","","","","" +"3","baseline","102","2026-09-29T02:19:28.8655899Z","102.0077192","0.999340200000006","5.078125","0","","","","","","","","","" +"3","baseline","103","2026-09-29T02:19:29.8785775Z","103.0207575","1.01303830000001","9.03846153846154","0","","","","","","","","","" +"3","baseline","104","2026-09-29T02:19:30.8631328Z","104.0051347","0.984377199999997","4.96031746031746","0","","","","","","","","","" +"3","baseline","105","2026-09-29T02:19:31.8663967Z","105.0084751","1.0033404","5.46875","0","","","","","","","","","" +"3","baseline","106","2026-09-29T02:19:32.8790608Z","106.0211372","1.0126621","9.42307692307692","0","","","","","","","","","" +"3","baseline","107","2026-09-29T02:19:33.8591554Z","107.0015092","0.980372000000003","4.96031746031746","0","","","","","","","","","" +"3","baseline","108","2026-09-29T02:19:34.8609082Z","108.0030021","1.00149289999999","6.4453125","0","","","","","","","","","" +"3","baseline","109","2026-09-29T02:19:35.8634795Z","109.0054622","1.00246010000001","4.6875","0","","","","","","","","","" +"3","baseline","110","2026-09-29T02:19:36.8637534Z","110.0058387","1.0003765","7.8125","0","","","","","","","","","" +"3","baseline","111","2026-09-29T02:19:37.8693439Z","111.0109844","1.0051457","8.3984375","0","","","","","","","","","" +"3","baseline","112","2026-09-29T02:19:38.8674116Z","112.0097211","0.998736699999995","5.6640625","0","","","","","","","","","" +"3","baseline","113","2026-09-29T02:19:39.8686967Z","113.0108102","1.0010891","5.46875","0","","","","","","","","","" +"3","baseline","114","2026-09-29T02:19:40.8726016Z","114.0147984","1.00398819999999","7.5","0","","","","","","","","","" +"3","baseline","115","2026-09-29T02:19:41.8715336Z","115.0136611","0.998862700000004","9.375","0","","","","","","","","","" +"3","baseline","116","2026-09-29T02:19:42.8696615Z","116.0117289","0.998067800000001","15.8730158730159","0","","","","","","","","","" +"3","baseline","117","2026-09-29T02:19:43.8747115Z","117.0169909","1.005262","10.7692307692308","0","","","","","","","","","" +"3","baseline","118","2026-09-29T02:19:44.8625457Z","118.0047226","0.987731699999998","7.14285714285714","0","","","","","","","","","" +"3","baseline","119","2026-09-29T02:19:45.8609462Z","119.0030456","0.998322999999999","7.421875","0","","","","","","","","","" +"3","baseline","120","2026-09-29T02:19:46.8615527Z","120.0037945","1.0007489","6.25","0","","","","","","","","","" +"3","baseline","121","2026-09-29T02:19:47.8671776Z","121.009234","1.00543949999999","5.078125","0","","","","","","","","","" +"3","baseline","122","2026-09-29T02:19:48.8706069Z","122.0127722","1.00353820000001","9.42307692307692","0","","","","","","","","","" +"3","baseline","123","2026-09-29T02:19:49.8713100Z","123.0133684","1.00059619999999","9.1796875","0","","","","","","","","","" +"3","baseline","124","2026-09-29T02:19:50.8691900Z","124.0113035","0.997935100000007","14.0873015873016","0","","","","","","","","","" +"3","baseline","125","2026-09-29T02:19:51.8689513Z","125.0110287","0.9997252","12.6953125","0","","","","","","","","","" +"3","baseline","126","2026-09-29T02:19:52.8808072Z","126.0228083","1.0117796","13.4615384615385","0","","","","","","","","","" +"3","baseline","127","2026-09-29T02:19:53.8646461Z","127.0067316","0.983923300000001","8.13492063492063","0","","","","","","","","","" +"3","baseline","128","2026-09-29T02:19:54.8681452Z","128.0102362","1.00350459999999","9.5703125","0","","","","","","","","","" +"3","baseline","129","2026-09-29T02:19:55.8724514Z","129.0151475","1.0049113","9.80769230769231","0","","","","","","","","","" +"3","baseline","130","2026-09-29T02:19:56.8678450Z","130.010059","0.994911500000001","7.93650793650794","0","","","","","","","","","" +"3","baseline","131","2026-09-29T02:19:57.8682533Z","131.0104011","1.00034210000001","7.8125","0","","","","","","","","","" +"3","baseline","132","2026-09-29T02:19:58.8672344Z","132.009374","0.998972900000012","8.59375","0","","","","","","","","","" +"3","baseline","133","2026-09-29T02:19:59.8682373Z","133.0103801","1.00100609999998","6.0546875","0","","","","","","","","","" +"3","baseline","134","2026-09-29T02:20:00.8655380Z","134.0076914","0.997311300000007","7.8125","0","","","","","","","","","" +"3","baseline","135","2026-09-29T02:20:01.8784999Z","135.0206838","1.0129924","7.69230769230769","0","","","","","","","","","" +"3","baseline","136","2026-09-29T02:20:02.8703136Z","136.0120481","0.991364299999987","10.3174603174603","0","","","","","","","","","" +"3","baseline","137","2026-09-29T02:20:03.8743651Z","137.0163703","1.00432220000002","9.03846153846154","0.194471953323342","","","","","","","","","" +"3","baseline","138","2026-09-29T02:20:04.8643948Z","138.0066179","0.990247599999975","39.2857142857143","0","","","","","","","","","" +"3","baseline","139","2026-09-29T02:20:05.8614490Z","139.0035623","0.996944400000018","77.5390625","0","","","","","","","","","" +"3","baseline","140","2026-09-29T02:20:06.8719369Z","140.0141212","1.01055890000001","86.9230769230769","0","","","","","","","","","" +"3","baseline","141","2026-09-29T02:20:07.8689535Z","141.0110299","0.996908699999977","24.8015873015873","0","","","","","","","","","" +"3","baseline","142","2026-09-29T02:20:08.8684150Z","142.0105469","0.999516999999997","19.7265625","0","","","","","","","","","" +"3","baseline","143","2026-09-29T02:20:09.8662160Z","143.0082789","0.997732000000013","21.875","0","","","","","","","","","" +"3","baseline","144","2026-09-29T02:20:10.8761850Z","144.018425","1.01014610000001","21.7307692307692","0","","","","","","","","","" +"3","baseline","145","2026-09-29T02:20:11.8597565Z","145.0018341","0.983409099999989","21.6269841269841","0","","","","","","","","","" +"3","baseline","146","2026-09-29T02:20:12.8740036Z","146.0160543","1.01422020000001","27.5","0","","","","","","","","","" +"3","baseline","147","2026-09-29T02:20:13.8707058Z","147.0128646","0.996810299999993","9.5703125","0","","","","","","","","","" +"3","baseline","148","2026-09-29T02:20:14.8674612Z","148.0096067","0.996742100000006","13.6904761904762","0","","","","","","","","","" +"3","baseline","149","2026-09-29T02:20:15.8669354Z","149.0089557","0.999348999999995","5.078125","0","","","","","","","","","" +"3","baseline","150","2026-09-29T02:20:16.8680634Z","150.0102054","1.00124969999999","9.375","0","","","","","","","","","" +"3","baseline","151","2026-09-29T02:20:17.8687859Z","151.0109135","1.0007081","7.6171875","0","","","","","","","","","" +"3","baseline","152","2026-09-29T02:20:18.8659629Z","152.0080481","0.99713460000001","3.515625","0","","","","","","","","","" +"3","baseline","153","2026-09-29T02:20:19.8664865Z","153.008747","1.0006989","10.3515625","0","","","","","","","","","" +"3","baseline","154","2026-09-29T02:20:20.8688968Z","154.0111044","1.00235739999999","13.4765625","0","","","","","","","","","" +"3","baseline","155","2026-09-29T02:20:21.8712968Z","155.0134141","1.00230970000001","7.30769230769231","0","","","","","","","","","" +"3","baseline","156","2026-09-29T02:20:22.8737911Z","156.0158824","1.00246829999998","7.6171875","0","","","","","","","","","" +"3","baseline","157","2026-09-29T02:20:23.8707402Z","157.0134149","0.997532500000005","15.0390625","0","","","","","","","","","" +"3","baseline","158","2026-09-29T02:20:24.8695488Z","158.0117382","0.99832330000001","15.4761904761905","0","","","","","","","","","" +"3","baseline","159","2026-09-29T02:20:25.8679063Z","159.0100412","0.998302999999993","7.2265625","0","","","","","","","","","" +"3","baseline","160","2026-09-29T02:20:26.8685860Z","160.0107509","1.00070970000002","7.03125","0","","","","","","","","","" +"3","baseline","161","2026-09-29T02:20:27.8811219Z","161.0232502","1.0124993","6.53846153846154","0","","","","","","","","","" +"3","baseline","162","2026-09-29T02:20:28.8687478Z","162.0108834","0.987633199999976","5.55555555555556","0","","","","","","","","","" +"3","baseline","163","2026-09-29T02:20:29.8673452Z","163.0094401","0.998556700000023","9.375","0","","","","","","","","","" +"3","baseline","164","2026-09-29T02:20:30.8724582Z","164.0145128","1.0050727","11.1538461538462","0","","","","","","","","","" +"3","baseline","165","2026-09-29T02:20:31.8679948Z","165.010147","0.995634199999984","28.5714285714286","0","","","","","","","","","" +"3","baseline","166","2026-09-29T02:20:32.8696618Z","166.0118394","1.0016924","41.6015625","0","","","","","","","","","" +"3","baseline","167","2026-09-29T02:20:33.8678941Z","167.0100451","0.9982057","14.84375","0","","","","","","","","","" +"3","baseline","168","2026-09-29T02:20:34.8671996Z","168.0096021","0.99955700000001","7.8125","0","","","","","","","","","" +"3","baseline","169","2026-09-29T02:20:35.8668593Z","169.0090622","0.999460099999993","7.421875","0","","","","","","","","","" +"3","baseline","170","2026-09-29T02:20:36.8803823Z","170.0224294","1.0133672","7.88461538461539","0","","","","","","","","","" +"3","baseline","171","2026-09-29T02:20:37.8665251Z","171.0087292","0.986299800000012","7.14285714285714","0","","","","","","","","","" +"3","baseline","172","2026-09-29T02:20:38.8654333Z","172.0075437","0.99881449999998","6.4453125","0","","","","","","","","","" +"3","baseline","173","2026-09-29T02:20:39.8668457Z","173.008918","1.00137430000001","5.078125","0","","","","","","","","","" +"3","baseline","174","2026-09-29T02:20:40.8776323Z","174.0196893","1.01077129999999","6.53846153846154","0","","","","","","","","","" +"3","baseline","175","2026-09-29T02:20:41.8617276Z","175.0038002","0.984110900000019","6.94444444444444","0","","","","","","","","","" +"3","baseline","176","2026-09-29T02:20:42.8663889Z","176.0084542","1.00465399999999","4.6875","0","","","","","","","","","" +"3","baseline","177","2026-09-29T02:20:43.8780683Z","177.0201504","1.01169620000002","6.53846153846154","0","","","","","","","","","" +"3","baseline","178","2026-09-29T02:20:44.8638292Z","178.0058593","0.985708899999992","4.76190476190476","0","","","","","","","","","" +"3","baseline","179","2026-09-29T02:20:45.8621078Z","179.003916","0.998056700000006","6.4453125","0","","","","","","","","","" +"3","baseline","180","2026-09-29T02:20:46.8621762Z","180.0043032","1.00038719999998","5.46875","0","","","","","","","","","" +"3","baseline","181","2026-09-29T02:20:47.8637825Z","181.0058158","1.00151260000001","6.8359375","0","","","","","","","","","" +"3","baseline","182","2026-09-29T02:20:48.8620878Z","182.0041291","0.998313300000007","5.859375","0","","","","","","","","","" +"3","baseline","183","2026-09-29T02:20:49.8610103Z","183.0026358","0.998506699999979","6.25","0","","","","","","","","","" +"3","baseline","184","2026-09-29T02:20:50.8631265Z","184.0047089","1.00207310000002","4.1015625","0","","","","","","","","","" +"3","baseline","185","2026-09-29T02:20:51.8643844Z","185.0071085","1.00239959999999","4.6875","0","","","","","","","","","" +"3","baseline","186","2026-09-29T02:20:52.8751816Z","186.0173024","1.01019390000002","10.1923076923077","0","","","","","","","","","" +"3","baseline","187","2026-09-29T02:20:53.8599664Z","187.0022484","0.984945999999979","9.92063492063492","0","","","","","","","","","" +"3","baseline","188","2026-09-29T02:20:54.8586497Z","188.0007707","0.998522300000019","8.203125","0","","","","","","","","","" +"3","baseline","189","2026-09-29T02:20:55.8594958Z","189.0015419","1.00077119999997","5.87084148727984","0","","","","","","","","","" +"3","baseline","190","2026-09-29T02:20:56.8728881Z","190.0150882","1.0135463","6.33397312859885","0","","","","","","","","","" +"3","baseline","191","2026-09-29T02:20:57.8730373Z","191.0151803","1.00009210000002","6.0546875","0","","","","","","","","","" +"3","baseline","192","2026-09-29T02:20:58.8730831Z","192.0146608","0.999480500000004","5.078125","0","","","","","","","","","" +"3","baseline","193","2026-09-29T02:20:59.8602853Z","193.0024831","0.987822299999976","5.35714285714286","0","","","","","","","","","" +"3","baseline","194","2026-09-29T02:21:00.8674645Z","194.0095087","1.00702560000002","6.4453125","0","","","","","","","","","" +"3","baseline","195","2026-09-29T02:21:01.8666975Z","195.0088831","0.999374399999994","10.9375","0","","","","","","","","","" +"3","baseline","196","2026-09-29T02:21:02.8692233Z","196.0113674","1.00248429999999","8.0078125","0","","","","","","","","","" +"3","baseline","197","2026-09-29T02:21:03.8700027Z","197.012264","1.0008966","8.984375","0","","","","","","","","","" +"3","baseline","198","2026-09-29T02:21:04.8717752Z","198.0140293","1.00176530000002","7.69230769230769","0.194968322420428","","","","","","","","","" +"3","baseline","199","2026-09-29T02:21:05.8733522Z","199.0154763","1.00144699999998","11.328125","0","","","","","","","","","" +"3","baseline","200","2026-09-29T02:21:06.8590304Z","200.0010891","0.985612800000013","6.54761904761905","0","","","","","","","","","" +"3","baseline","201","2026-09-29T02:21:07.8719616Z","201.0141928","1.01310369999999","9.61538461538461","0","","","","","","","","","" +"3","baseline","202","2026-09-29T02:21:08.8719329Z","202.0141149","0.999922099999992","6.25","0","","","","","","","","","" +"3","baseline","203","2026-09-29T02:21:09.8687914Z","203.0108933","0.996778400000011","10.1190476190476","0","","","","","","","","","" +"3","baseline","204","2026-09-29T02:21:10.8662996Z","204.0086157","0.997722399999986","7.421875","0","","","","","","","","","" +"3","baseline","205","2026-09-29T02:21:11.8810567Z","205.023172","1.01455630000001","5.76923076923077","0","","","","","","","","","" +"3","baseline","206","2026-09-29T02:21:12.8676942Z","206.0093705","0.9861985","6.74603174603175","0","","","","","","","","","" +"3","baseline","207","2026-09-29T02:21:13.8654925Z","207.007707","0.998336499999994","11.1328125","0","","","","","","","","","" +"3","baseline","208","2026-09-29T02:21:14.8679206Z","208.0100074","1.0023004","6.4453125","0","","","","","","","","","" +"3","baseline","209","2026-09-29T02:21:15.8655596Z","209.0076362","0.997628800000001","7.04500978473581","0","","","","","","","","","" +"3","baseline","210","2026-09-29T02:21:16.8798366Z","210.0220245","1.01438830000001","7.86948176583493","0","","","","","","","","","" +"3","baseline","211","2026-09-29T02:21:17.8616894Z","211.0037276","0.981703100000004","6.54761904761905","0","","","","","","","","","" +"3","baseline","212","2026-09-29T02:21:18.8624610Z","212.0045064","1.00077880000001","6.25","0","","","","","","","","","" +"3","baseline","213","2026-09-29T02:21:19.8654540Z","213.0075437","1.00303729999999","6.8359375","0","","","","","","","","","" +"3","baseline","214","2026-09-29T02:21:20.8678732Z","214.0100048","1.0024611","7.6171875","0","","","","","","","","","" +"3","baseline","215","2026-09-29T02:21:21.8655854Z","215.0077467","0.997741899999994","8.3984375","0","","","","","","","","","" +"3","baseline","216","2026-09-29T02:21:22.8676165Z","216.0096859","1.00193920000001","6.25","0","","","","","","","","","" +"3","baseline","217","2026-09-29T02:21:23.8698747Z","217.0119491","1.00226319999999","11.1328125","0","","","","","","","","","" +"3","baseline","218","2026-09-29T02:21:24.8723009Z","218.0144353","1.00248620000002","15.3846153846154","0.194828118332198","","","","","","","","","" +"3","baseline","219","2026-09-29T02:21:25.8580882Z","219.0000857","0.985650399999997","6.34920634920635","0","","","","","","","","","" +"3","baseline","220","2026-09-29T02:21:26.8591131Z","220.0012321","1.00114639999998","5.078125","0","","","","","","","","","" +"3","baseline","221","2026-09-29T02:21:27.8620585Z","221.0041301","1.00289800000002","5.859375","0","","","","","","","","","" +"3","baseline","222","2026-09-29T02:21:28.8613673Z","222.0034692","0.999339099999986","5.6640625","0","","","","","","","","","" +"3","baseline","223","2026-09-29T02:21:29.8658763Z","223.0079527","1.00448349999999","5.859375","0","","","","","","","","","" +"3","baseline","224","2026-09-29T02:21:30.8673843Z","224.0095296","1.0015769","4.1015625","0","","","","","","","","","" +"3","baseline","225","2026-09-29T02:21:31.8716935Z","225.0136877","1.00415810000001","10.7692307692308","0","","","","","","","","","" +"3","baseline","226","2026-09-29T02:21:32.8732673Z","226.0147823","1.00109459999999","5.46875","0","","","","","","","","","" +"3","baseline","227","2026-09-29T02:21:33.8588155Z","227.0004215","0.985639200000008","7.34126984126984","0","","","","","","","","","" +"3","baseline","228","2026-09-29T02:21:34.8733450Z","228.0155141","1.0150926","8.46153846153846","0","","","","","","","","","" +"3","baseline","229","2026-09-29T02:21:35.8642843Z","229.0063316","0.990817499999991","8.73015873015873","0","","","","","","","","","" +"3","baseline","230","2026-09-29T02:21:36.8618727Z","230.0039793","0.997647700000016","8.59375","0","","","","","","","","","" +"3","baseline","231","2026-09-29T02:21:37.8626389Z","231.0047182","1.00073889999999","6.640625","0","","","","","","","","","" +"3","baseline","232","2026-09-29T02:21:38.8640079Z","232.0062616","1.0015434","4.296875","0","","","","","","","","","" +"3","baseline","233","2026-09-29T02:21:39.8698701Z","233.0119016","1.00564","6.4453125","0","","","","","","","","","" +"3","baseline","234","2026-09-29T02:21:40.8767181Z","234.018781","1.0068794","8.84615384615385","0","","","","","","","","","" +"3","baseline","235","2026-09-29T02:21:41.8603724Z","235.0030752","0.984294199999994","7.53968253968254","0","","","","","","","","","" +"3","baseline","236","2026-09-29T02:21:42.8667035Z","236.0089538","1.00587860000002","4.6875","0","","","","","","","","","" +"3","baseline","237","2026-09-29T02:21:43.8684320Z","237.0104903","1.00153649999999","6.0546875","0","","","","","","","","","" +"3","baseline","238","2026-09-29T02:21:44.8672014Z","238.0088045","0.99831420000001","10.546875","0","","","","","","","","","" +"3","baseline","239","2026-09-29T02:21:45.8669102Z","239.0089175","1.000113","9.1796875","0","","","","","","","","","" +"3","baseline","240","2026-09-29T02:21:46.8688539Z","240.0110414","1.00212389999999","7.421875","0","","","","","","","","","" +"3","baseline","241","2026-09-29T02:21:47.8680428Z","241.0096989","0.998657500000007","8.984375","0","","","","","","","","","" +"3","baseline","242","2026-09-29T02:21:48.8674802Z","242.0097691","1.00007020000001","6.4453125","0","","","","","","","","","" +"3","baseline","243","2026-09-29T02:21:49.8695585Z","243.0116422","1.00187309999998","13.28125","0","","","","","","","","","" +"3","baseline","244","2026-09-29T02:21:50.8704015Z","244.0125581","1.0009159","9.03846153846154","0","","","","","","","","","" +"3","baseline","245","2026-09-29T02:21:51.8789211Z","245.0209806","1.00842250000002","11.328125","0","","","","","","","","","" +"3","baseline","246","2026-09-29T02:21:52.8641417Z","246.0062653","0.985284699999994","6.15079365079365","0","","","","","","","","","" +"3","baseline","247","2026-09-29T02:21:53.8617016Z","247.0037796","0.997514300000006","7.8125","0","","","","","","","","","" +"3","baseline","248","2026-09-29T02:21:54.8632008Z","248.005235","1.0014554","8.02348336594912","0","","","","","","","","","" +"3","baseline","249","2026-09-29T02:21:55.8606329Z","249.0028402","0.997605199999981","11.5009746588694","0","","","","","","","","","" +"3","baseline","250","2026-09-29T02:21:56.8598841Z","250.0021315","0.99929130000001","7.03125","0","","","","","","","","","" +"3","baseline","251","2026-09-29T02:21:57.8624237Z","251.0044449","1.00231339999999","6.0546875","0","","","","","","","","","" +"3","baseline","252","2026-09-29T02:21:58.8638953Z","252.0060875","1.0016426","5.2734375","0","","","","","","","","","" +"3","baseline","253","2026-09-29T02:21:59.8616286Z","253.0038253","0.99773780000001","5.6640625","0","","","","","","","","","" +"3","baseline","254","2026-09-29T02:22:00.8580877Z","254.0002276","0.9964023","5.859375","0","","","","","","","","","" +"3","baseline","255","2026-09-29T02:22:01.8643875Z","255.0064665","1.0062389","5.6640625","0","","","","","","","","","" +"3","baseline","256","2026-09-29T02:22:02.8721408Z","256.0145179","1.0080514","7.69230769230769","0","","","","","","","","","" +"3","baseline","257","2026-09-29T02:22:03.8713532Z","257.0134296","0.998911700000008","7.421875","0","","","","","","","","","" +"3","baseline","258","2026-09-29T02:22:04.8902483Z","258.0257021","1.01227249999999","15.4142581888247","0","","","","","","","","","" +"3","baseline","259","2026-09-29T02:22:05.8710715Z","259.0132641","0.987562000000025","22.5742574257426","0","","","","","","","","","" +"3","baseline","260","2026-09-29T02:22:06.8710805Z","260.0132122","0.999948099999983","9.765625","0","","","","","","","","","" +"3","baseline","261","2026-09-29T02:22:07.8758445Z","261.0179912","1.00477899999998","9.5703125","0","","","","","","","","","" +"3","baseline","262","2026-09-29T02:22:08.8607053Z","262.0028623","0.984871100000021","5.35714285714286","0","","","","","","","","","" +"3","baseline","263","2026-09-29T02:22:09.8639952Z","263.0059879","1.00312559999998","9.1796875","0","","","","","","","","","" +"3","baseline","264","2026-09-29T02:22:10.8740854Z","264.0162051","1.0102172","7.88461538461539","0","","","","","","","","","" +"3","baseline","265","2026-09-29T02:22:11.8634856Z","265.0055843","0.98937920000003","5.95238095238095","0","","","","","","","","","" +"3","baseline","266","2026-09-29T02:22:12.8604092Z","266.0024512","0.996866899999986","7.421875","0","","","","","","","","","" +"3","baseline","267","2026-09-29T02:22:13.8635173Z","267.0056104","1.00315919999997","8.0078125","0","","","","","","","","","" +"3","baseline","268","2026-09-29T02:22:14.8657046Z","268.0079817","1.00237130000005","8.3984375","0","","","","","","","","","" +"3","baseline","269","2026-09-29T02:22:15.8804053Z","269.0224731","1.0144914","8.84615384615385","0","","","","","","","","","" +"3","baseline","270","2026-09-29T02:22:16.8629140Z","270.0050867","0.982613599999979","6.74603174603175","0","","","","","","","","","" +"3","baseline","271","2026-09-29T02:22:17.8671791Z","271.0095048","1.00441810000001","7.8125","0","","","","","","","","","" +"3","baseline","272","2026-09-29T02:22:18.8676372Z","272.0098286","1.00032379999999","5.6640625","0","","","","","","","","","" +"3","baseline","273","2026-09-29T02:22:19.8685271Z","273.0105616","1.00073299999997","3.7109375","0","","","","","","","","","" +"3","baseline","274","2026-09-29T02:22:20.8685953Z","274.0114112","1.00084960000004","7.2265625","0","","","","","","","","","" +"3","baseline","275","2026-09-29T02:22:21.8735095Z","275.0153174","1.00390620000002","8.65384615384615","0","","","","","","","","","" +"3","baseline","276","2026-09-29T02:22:22.8596585Z","276.002009","0.986691599999972","6.15079365079365","0","","","","","","","","","" +"3","baseline","277","2026-09-29T02:22:23.8643192Z","277.0065326","1.00452360000003","4.4921875","0","","","","","","","","","" +"3","baseline","278","2026-09-29T02:22:24.8643337Z","278.0063829","0.999850299999991","8.7890625","0","","","","","","","","","" +"3","baseline","279","2026-09-29T02:22:25.8634434Z","279.0056779","0.999294999999961","6.0546875","0","","","","","","","","","" +"3","baseline","280","2026-09-29T02:22:26.8637403Z","280.0058894","1.00021150000003","5.2734375","0","","","","","","","","","" +"3","baseline","281","2026-09-29T02:22:27.8618803Z","281.0040367","0.998147299999971","7.03125","0","","","","","","","","","" +"3","baseline","282","2026-09-29T02:22:28.8621498Z","282.0043271","1.00029040000004","7.421875","0","","","","","","","","","" +"3","baseline","283","2026-09-29T02:22:29.8621474Z","283.0041956","0.999868499999991","6.8359375","0","","","","","","","","","" +"3","baseline","284","2026-09-29T02:22:30.8599659Z","284.0021618","0.997966200000008","5.46875","0","","","","","","","","","" +"3","baseline","285","2026-09-29T02:22:31.8607152Z","285.0027369","1.00057509999999","5.46875","0","","","","","","","","","" +"3","baseline","286","2026-09-29T02:22:32.8603592Z","286.0026923","0.999955399999976","7.8125","0","","","","","","","","","" +"3","baseline","287","2026-09-29T02:22:33.8621766Z","287.0042802","1.0015879","7.03125","0","","","","","","","","","" +"3","baseline","288","2026-09-29T02:22:34.8611180Z","288.0032215","0.998941300000013","9.375","0","","","","","","","","","" +"3","baseline","289","2026-09-29T02:22:35.8625951Z","289.0046462","1.00142469999997","6.0546875","0","","","","","","","","","" +"3","baseline","290","2026-09-29T02:22:36.8603318Z","290.0025108","0.997864600000014","9.9609375","0","","","","","","","","","" +"3","baseline","291","2026-09-29T02:22:37.8602713Z","291.0023403","0.999829500000033","6.0546875","0","","","","","","","","","" +"3","baseline","292","2026-09-29T02:22:38.8615391Z","292.0036547","1.00131439999996","5.46875","0","","","","","","","","","" +"3","baseline","293","2026-09-29T02:22:39.8672034Z","293.0093825","1.00572779999999","4.6875","0","","","","","","","","","" +"3","baseline","294","2026-09-29T02:22:40.8653929Z","294.0075278","0.998145300000033","10.7421875","0","","","","","","","","","" +"3","baseline","295","2026-09-29T02:22:41.8672696Z","295.0093114","1.00178360000001","7.03125","0","","","","","","","","","" +"3","baseline","296","2026-09-29T02:22:42.8757546Z","296.0180145","1.00870309999999","5.76923076923077","0","","","","","","","","","" +"3","baseline","297","2026-09-29T02:22:43.8626049Z","297.004676","0.986661499999968","6.54761904761905","0","","","","","","","","","" +"3","baseline","298","2026-09-29T02:22:44.8578628Z","298.0000127","0.995336700000053","9.1796875","0","","","","","","","","","" +"3","baseline","299","2026-09-29T02:22:45.8728185Z","299.0152267","1.01521399999996","6.73076923076923","0","","","","","","","","","" +"3","baseline","300","2026-09-29T02:22:46.8621542Z","300.0044275","0.989200799999992","5.35714285714286","0","","","","","","","","","" diff --git a/docs/benchmarks/2026-09-29-v0.2.0/results/run-3-block-end.json b/docs/benchmarks/2026-09-29-v0.2.0/results/run-3-block-end.json new file mode 100644 index 0000000..0b844dd --- /dev/null +++ b/docs/benchmarks/2026-09-29-v0.2.0/results/run-3-block-end.json @@ -0,0 +1,12 @@ +{ + "state": "block", + "layers": { + "shutdown": true, + "local_shutdown": true, + "update": false, + "remote": true, + "sleep": true + }, + "uptime_seconds": 333, + "blocked_count": 0 +} diff --git a/docs/benchmarks/2026-09-29-v0.2.0/results/run-3-block-start.json b/docs/benchmarks/2026-09-29-v0.2.0/results/run-3-block-start.json new file mode 100644 index 0000000..6e48522 --- /dev/null +++ b/docs/benchmarks/2026-09-29-v0.2.0/results/run-3-block-start.json @@ -0,0 +1,12 @@ +{ + "state": "block", + "layers": { + "shutdown": true, + "local_shutdown": true, + "update": false, + "remote": true, + "sleep": true + }, + "uptime_seconds": 32, + "blocked_count": 0 +} diff --git a/docs/benchmarks/2026-09-29-v0.2.0/results/run-3-block.csv b/docs/benchmarks/2026-09-29-v0.2.0/results/run-3-block.csv new file mode 100644 index 0000000..c650438 --- /dev/null +++ b/docs/benchmarks/2026-09-29-v0.2.0/results/run-3-block.csv @@ -0,0 +1,301 @@ +"run","scenario","sample","utc","elapsed_s","interval_s","system_cpu_pct","collector_cpu_pct","pid","process_cpu_s","cpu_pct","private_mib","working_set_mib","threads","handles","io_read_bytes_s","io_write_bytes_s" +"3","block","1","2026-09-29T02:12:17.7852724Z","1.0039688","1.0039651","10.3515625","0","24176","0","0","3.2890625","14.0859375","11","194","0","0" +"3","block","2","2026-09-29T02:12:18.7881578Z","2.0067596","1.0027908","6.0546875","0","24176","0","0","3.2890625","14.0859375","11","194","0","0" +"3","block","3","2026-09-29T02:12:19.8004849Z","3.0191655","1.0124059","6.15384615384615","0","24176","0","0","3.2890625","14.0859375","11","194","0","0" +"3","block","4","2026-09-29T02:12:20.7875314Z","4.0063625","0.987197","4.36507936507936","0","24176","0","0","3.2890625","14.0859375","11","194","0","0" +"3","block","5","2026-09-29T02:12:21.8019011Z","5.0205974","1.0142349","7.11538461538461","0","24176","0","0","3.2890625","14.0859375","11","194","0","0" +"3","block","6","2026-09-29T02:12:22.7892409Z","6.0079452","0.9873478","6.34920634920635","0","24176","0","0","3.2890625","14.0859375","11","194","0","0" +"3","block","7","2026-09-29T02:12:23.7910993Z","7.009769","1.0018238","8.203125","0.19495693753732","24176","0","0","3.2890625","14.0859375","11","194","0","0" +"3","block","8","2026-09-29T02:12:24.7907605Z","8.0095073","0.9997383","6.0546875","0.195363626661097","24176","0","0","3.2890625","14.0859375","11","194","0","0" +"3","block","9","2026-09-29T02:12:25.7967977Z","9.0150351","1.0055278","6.15384615384615","0.194238786834138","24176","0","0","3.2890625","14.0859375","11","194","0","0" +"3","block","10","2026-09-29T02:12:26.7964227Z","10.0145088","0.999473699999999","7.2265625","0","24176","0","0","3.2890625","14.0859375","11","194","0","0" +"3","block","11","2026-09-29T02:12:27.7999348Z","11.0186482","1.0041394","10.3515625","0","24176","0","0","3.2890625","14.0859375","11","194","0","0" +"3","block","12","2026-09-29T02:12:28.7836420Z","12.0024171","0.983768899999999","5.35714285714286","0","24176","0","0","3.2890625","14.0859375","11","194","0","0" +"3","block","13","2026-09-29T02:12:29.7998174Z","13.0184281","1.016011","4.03846153846154","0","24176","0","0","3.2890625","14.0859375","11","194","0","0" +"3","block","14","2026-09-29T02:12:30.7821479Z","14.0004308","0.982002700000001","5.35714285714286","0","24176","0","0","3.2890625","14.0859375","11","194","0","0" +"3","block","15","2026-09-29T02:12:31.7943456Z","15.0129232","1.0124924","12.5","0","24176","0","0","3.2890625","14.0859375","11","194","0","0" +"3","block","16","2026-09-29T02:12:32.7886841Z","16.0072115","0.994288300000001","39.2857142857143","0","24176","0","0","3.2890625","14.0859375","11","194","0","0" +"3","block","17","2026-09-29T02:12:33.7984656Z","17.0172974","1.0100859","22.8846153846154","0","24176","0","0","3.2890625","14.0859375","11","194","0","0" +"3","block","18","2026-09-29T02:12:34.8005160Z","18.019338","1.0020406","13.671875","0","24176","0","0","3.2890625","14.0859375","11","194","0","0" +"3","block","19","2026-09-29T02:12:35.8000599Z","19.0187003","0.999362300000001","9.765625","0","24176","0","0","3.2890625","14.0859375","11","194","0","0" +"3","block","20","2026-09-29T02:12:36.7833436Z","20.0019725","0.983272200000002","5.95238095238095","0","24176","0","0","3.2890625","14.0859375","11","194","0","0" +"3","block","21","2026-09-29T02:12:37.7974256Z","21.0160867","1.0141142","8.26923076923077","0","24176","0","0","3.2890625","14.0859375","11","194","0","0" +"3","block","22","2026-09-29T02:12:38.7823570Z","22.0010012","0.984914499999999","4.76190476190476","0","24176","0","0","3.2890625","14.0859375","11","194","0","0" +"3","block","23","2026-09-29T02:12:39.7821996Z","23.0009087","0.999907500000003","7.6171875","0","24176","0","0","3.2890625","14.0859375","11","194","0","0" +"3","block","24","2026-09-29T02:12:40.7967788Z","24.01555","1.0146413","5.96153846153846","0","24176","0","0","3.2890625","14.0859375","11","194","0","0" +"3","block","25","2026-09-29T02:12:41.7932103Z","25.0118896","0.996339600000002","8.203125","0","24176","0","0","3.2890625","14.0859375","11","194","0","0" +"3","block","26","2026-09-29T02:12:42.7916722Z","26.0102281","0.998338499999999","8.33333333333333","0","24176","0","0","3.2890625","14.0859375","11","194","0","0" +"3","block","27","2026-09-29T02:12:43.7979560Z","27.0166004","1.0063723","14.6153846153846","0.194075790838043","24176","0.015625","0.194075790838043","3.26171875","14.07421875","10","194","0","0" +"3","block","28","2026-09-29T02:12:44.7968472Z","28.0155434","0.998943000000001","5.859375","0","24176","0","0","3.26171875","14.07421875","10","194","0","0" +"3","block","29","2026-09-29T02:12:45.7955831Z","29.0144031","0.998859700000001","6.25","0","24176","0","0","3.26171875","14.07421875","10","194","0","0" +"3","block","30","2026-09-29T02:12:46.7907358Z","30.0093874","0.994984299999999","9.12698412698413","0","24176","0","0","3.26171875","14.07421875","10","194","0","0" +"3","block","31","2026-09-29T02:12:47.7945731Z","31.0133327","1.0039453","14.8076923076923","0.194544961762359","24176","0","0","3.26171875","14.07421875","10","194","0","0" +"3","block","32","2026-09-29T02:12:48.7827086Z","32.0014901","0.988157399999999","5.75396825396825","0","24176","0","0","3.26171875","14.07421875","10","194","0","0" +"3","block","33","2026-09-29T02:12:49.7830634Z","33.0016454","1.0001553","9.19765166340509","0","24176","0","0","3.26171875","14.07421875","10","194","0","0" +"3","block","34","2026-09-29T02:12:50.7860168Z","34.0047785","1.0031331","6.23781676413255","0","24176","0","0","3.26171875","14.07421875","10","194","0","0" +"3","block","35","2026-09-29T02:12:51.8021073Z","35.0209564","1.0161779","5.96153846153846","0","24176","0","0","3.26171875","14.07421875","10","194","0","0" +"3","block","36","2026-09-29T02:12:52.7886065Z","36.0072458","0.986289400000004","4.76190476190476","0","24176","0","0","3.26171875","14.07421875","10","194","0","0" +"3","block","37","2026-09-29T02:12:53.7919936Z","37.0107639","1.0035181","10.8527131782946","0.194627780007157","24176","0","0","3.26171875","14.07421875","10","194","0","0" +"3","block","38","2026-09-29T02:12:54.7994564Z","38.0180532","1.0072893","7.94573643410853","0","24176","0","0","3.26171875","14.07421875","10","194","0","0" +"3","block","39","2026-09-29T02:12:55.8012158Z","39.0199128","1.0018596","7.6171875","0","24176","0","0","3.26171875","14.07421875","10","194","0","0" +"3","block","40","2026-09-29T02:12:56.7920565Z","40.0107399","0.990827099999997","8.0078125","0.197120668177123","24176","0","0","3.26171875","14.07421875","10","194","0","0" +"3","block","41","2026-09-29T02:12:57.7967936Z","41.0150124","1.0042725","9.765625","0","24176","0","0","3.26171875","14.07421875","10","194","0","0" +"3","block","42","2026-09-29T02:12:58.7838099Z","42.0025755","0.987563100000003","5.95238095238095","0","24176","0","0","3.26171875","14.07421875","10","194","0","0" +"3","block","43","2026-09-29T02:12:59.7831013Z","43.001708","0.999132500000002","6.640625","0","24176","0","0","3.26171875","14.07421875","10","194","0","0" +"3","block","44","2026-09-29T02:13:00.7855723Z","44.004181","1.00247299999999","6.25","0","24176","0","0","3.26171875","14.07421875","10","194","0","0" +"3","block","45","2026-09-29T02:13:01.7983689Z","45.017035","1.012854","11.5384615384615","0","24176","0","0","3.26171875","14.07421875","10","194","0","0" +"3","block","46","2026-09-29T02:13:02.7815286Z","46.0002265","0.983191499999997","4.56349206349206","0","24176","0","0","3.26171875","14.07421875","10","194","0","0" +"3","block","47","2026-09-29T02:13:03.7952681Z","47.0140665","1.01384","9.42307692307692","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","48","2026-09-29T02:13:04.7947503Z","48.0133814","0.999314900000002","4.8828125","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","49","2026-09-29T02:13:05.7940165Z","49.0126012","0.999219799999999","7.421875","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","50","2026-09-29T02:13:06.7909685Z","50.0096251","0.997023900000002","13.0952380952381","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","51","2026-09-29T02:13:07.8009450Z","51.0195744","1.0099493","26.5384615384615","0.193388420587054","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","52","2026-09-29T02:13:08.7977484Z","52.0163703","0.996795900000002","7.8125","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","53","2026-09-29T02:13:09.7844189Z","53.0031189","0.986748599999999","5.55555555555556","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","54","2026-09-29T02:13:10.7914955Z","54.0101631","1.0070442","9.9609375","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","55","2026-09-29T02:13:11.7878746Z","55.0066981","0.996534999999994","7.8125","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","56","2026-09-29T02:13:12.7883706Z","56.0071745","1.0004764","6.0546875","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","57","2026-09-29T02:13:13.7890121Z","57.0076895","1.000515","6.640625","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","58","2026-09-29T02:13:14.7986395Z","58.0172797","1.0095902","4.80769230769231","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","59","2026-09-29T02:13:15.7839767Z","59.0026277","0.985348000000002","8.92857142857143","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","60","2026-09-29T02:13:16.7829655Z","60.0016495","0.999021800000001","3.90625","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","61","2026-09-29T02:13:17.7880398Z","61.0068992","1.0052497","5.078125","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","62","2026-09-29T02:13:18.7909688Z","62.0096371","1.0027379","10.546875","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","63","2026-09-29T02:13:19.7896544Z","63.0083323","0.9986952","9.765625","0.195567676704564","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","64","2026-09-29T02:13:20.8011193Z","64.0196726","1.01134029999999","6.73076923076923","0.193122433665504","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","65","2026-09-29T02:13:21.8007784Z","65.0193905","0.999717900000007","4.6875","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","66","2026-09-29T02:13:22.8013559Z","66.0200024","1.0006119","4.8828125","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","67","2026-09-29T02:13:23.8022242Z","67.0206511","1.0006487","6.8359375","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","68","2026-09-29T02:13:24.8013958Z","68.0199474","0.999296299999997","7.03125","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","69","2026-09-29T02:13:25.7996424Z","69.0182542","0.998306800000009","7.03125","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","70","2026-09-29T02:13:26.7989024Z","70.0174781","0.99922389999999","7.421875","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","71","2026-09-29T02:13:27.7838693Z","71.0025767","0.985098600000001","7.53968253968254","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","72","2026-09-29T02:13:28.7949724Z","72.0136069","1.01103020000001","7.5","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","73","2026-09-29T02:13:29.7855419Z","73.0043464","0.990739500000004","5.15873015873016","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","74","2026-09-29T02:13:30.7979429Z","74.0165506","1.0122042","5.19230769230769","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","75","2026-09-29T02:13:31.7967346Z","75.0148863","0.998335699999998","6.4453125","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","76","2026-09-29T02:13:32.7925928Z","76.0112793","0.996392999999998","6.8359375","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","77","2026-09-29T02:13:33.7902013Z","77.0089125","0.997633199999996","12.5","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","78","2026-09-29T02:13:34.8009825Z","78.0196813","1.01076880000001","6.34615384615385","0.193231627252442","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","79","2026-09-29T02:13:35.8030771Z","79.0216325","1.00195119999999","9.1796875","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","80","2026-09-29T02:13:36.7917535Z","80.0110469","0.989414400000001","9.1796875","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","81","2026-09-29T02:13:37.7914525Z","81.0100833","0.999036399999994","13.2936507936508","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","82","2026-09-29T02:13:38.7918938Z","82.0106265","1.00054320000001","13.8671875","0.195206463848835","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","83","2026-09-29T02:13:39.7961807Z","83.0149393","1.00431279999999","8.07692307692308","0.194473773509609","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","84","2026-09-29T02:13:40.7974278Z","84.0160652","1.00112590000001","5.859375","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","85","2026-09-29T02:13:41.7820608Z","85.0008491","0.984783899999996","4.37375745526839","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","86","2026-09-29T02:13:42.7849494Z","86.0036184","1.0027693","6.23781676413255","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","87","2026-09-29T02:13:43.7957179Z","87.0145325","1.01091410000001","6.34615384615385","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","88","2026-09-29T02:13:44.7937994Z","88.0126495","0.998116999999993","9.1796875","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","89","2026-09-29T02:13:45.7923684Z","89.0111573","0.998507799999999","14.0625","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","90","2026-09-29T02:13:46.7927967Z","90.0115496","1.0003923","9.1796875","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","91","2026-09-29T02:13:47.7900816Z","91.0086839","0.997134299999999","8.53174603174603","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","92","2026-09-29T02:13:48.7902092Z","92.0090321","1.0003482","11.9140625","0.195244515859577","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","93","2026-09-29T02:13:49.7894276Z","93.0081405","0.999108399999997","10.546875","0.195486796027339","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","94","2026-09-29T02:13:50.7882134Z","94.0068187","0.998678200000001","11.328125","0.195571005755407","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","95","2026-09-29T02:13:51.7874040Z","95.0062095","0.9993908","5.2734375","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","96","2026-09-29T02:13:52.7883712Z","96.0071303","1.0009208","6.4453125","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","97","2026-09-29T02:13:53.7872831Z","97.0059433","0.998812999999998","6.25","0.195544611453796","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","98","2026-09-29T02:13:54.8027902Z","98.0215179","1.01557459999999","5.76923076923077","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","99","2026-09-29T02:13:55.8015507Z","99.0201193","0.998601399999998","7.03125","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","100","2026-09-29T02:13:56.7987379Z","100.0168571","0.996737800000005","6.4453125","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","101","2026-09-29T02:13:57.7819705Z","101.0006234","0.983766299999999","7.14285714285714","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","102","2026-09-29T02:13:58.7876702Z","102.006551","1.00592760000001","10.3515625","0","24176","0","0","3.23046875","14.0625","9","192","0","0" +"3","block","103","2026-09-29T02:13:59.7875769Z","103.0061685","0.999617499999999","5.859375","0","24176","0","0","3.23046875","14.0625","9","192","0","0" +"3","block","104","2026-09-29T02:14:00.7875925Z","104.0063083","1.0001398","5.6640625","0","24176","0","0","3.23046875","14.0625","9","192","0","0" +"3","block","105","2026-09-29T02:14:01.7984879Z","105.0171643","1.01085599999999","4.03846153846154","0","24176","0","0","3.23046875","14.0625","9","192","0","0" +"3","block","106","2026-09-29T02:14:02.7814077Z","106.0001153","0.982951","6.15079365079365","0","24176","0","0","3.23046875","14.0625","9","192","0","0" +"3","block","107","2026-09-29T02:14:03.7824366Z","107.0010227","1.0009074","7.6171875","0","24176","0","0","3.23046875","14.0625","9","192","0","0" +"3","block","108","2026-09-29T02:14:04.7863311Z","108.0051919","1.00416920000001","5.46875","0","24176","0","0","3.23046875","14.0625","9","192","0","0" +"3","block","109","2026-09-29T02:14:05.7871443Z","109.0057338","1.0005419","6.4453125","0","24176","0","0","3.23046875","14.0625","9","192","0","0" +"3","block","110","2026-09-29T02:14:06.8018626Z","110.0205729","1.01483909999999","7.11538461538461","0","24176","0","0","3.23046875","14.0625","9","192","0","0" +"3","block","111","2026-09-29T02:14:07.7969200Z","111.0154728","0.994899900000007","9.375","0","24176","0","0","3.23046875","14.0625","9","192","0","0" +"3","block","112","2026-09-29T02:14:08.7851207Z","112.0037935","0.988320700000003","8.92857142857143","0","24176","0","0","3.23046875","14.0625","9","192","0","0" +"3","block","113","2026-09-29T02:14:09.8023528Z","113.0209554","1.01716189999999","6.15384615384615","0","24176","0","0","3.23046875","14.0625","9","192","0","0" +"3","block","114","2026-09-29T02:14:10.8009376Z","114.019543","0.998587600000008","8.203125","0","24176","0","0","3.23046875","14.0625","9","192","0","0" +"3","block","115","2026-09-29T02:14:11.7948619Z","115.0135017","0.993958699999993","7.421875","0","24176","0","0","3.23046875","14.0625","9","192","0","0" +"3","block","116","2026-09-29T02:14:12.7833485Z","116.0021901","0.988688400000001","4.76190476190476","0","24176","0","0","3.23046875","14.0625","9","192","0","0" +"3","block","117","2026-09-29T02:14:13.7900608Z","117.0088371","1.006647","8.7890625","0","24176","0","0","3.26171875","14.078125","10","192","0","0" +"3","block","118","2026-09-29T02:14:14.7959090Z","118.0147078","1.0058707","8.07692307692308","0.19417257108692","24176","0","0","3.26171875","14.078125","10","192","0","0" +"3","block","119","2026-09-29T02:14:15.7944133Z","119.01256","0.997852199999997","6.25","0","24176","0","0","3.26171875","14.078125","10","192","0","0" +"3","block","120","2026-09-29T02:14:16.7933980Z","120.0121488","0.999588799999998","9.5703125","0","24176","0","0","3.26171875","14.078125","10","192","0","0" +"3","block","121","2026-09-29T02:14:17.7888819Z","121.0076165","0.995467700000006","21.6269841269841","0","24176","0","0","3.26171875","14.078125","10","192","0","0" +"3","block","122","2026-09-29T02:14:18.8016882Z","122.0208557","1.0132392","26.5384615384615","0.192760505120607","24176","0","0","3.26171875","14.078125","10","192","0","0" +"3","block","123","2026-09-29T02:14:19.7962813Z","123.0151037","0.994247999999999","26.953125","0","24176","0","0","3.26171875","14.078125","10","192","0","0" +"3","block","124","2026-09-29T02:14:20.7950962Z","124.013633","0.998529300000001","15.625","0","24176","0","0","3.26171875","14.078125","10","192","0","0" +"3","block","125","2026-09-29T02:14:21.7949882Z","125.0136441","1.00001109999999","6.4453125","0","24176","0","0","3.26171875","14.078125","10","192","0","0" +"3","block","126","2026-09-29T02:14:22.7940735Z","126.0127107","0.999066600000006","11.5234375","0","24176","0","0","3.26171875","14.078125","10","192","0","0" +"3","block","127","2026-09-29T02:14:23.7950307Z","127.013614","1.00090329999999","6.640625","0","24176","0","0","3.26171875","14.078125","10","192","0","0" +"3","block","128","2026-09-29T02:14:24.7885767Z","128.0072488","0.993634799999995","33.1349206349206","0","24176","0","0","3.26171875","14.078125","10","192","0","0" +"3","block","129","2026-09-29T02:14:25.7992297Z","129.017923","1.01067420000001","21.1538461538462","0.193249713903845","24176","0","0","3.26171875","14.078125","10","192","0","0" +"3","block","130","2026-09-29T02:14:26.7947481Z","130.0133306","0.995407599999993","15.234375","0","24176","0","0","3.26171875","14.078125","10","192","0","0" +"3","block","131","2026-09-29T02:14:27.7926243Z","131.0113782","0.998047600000007","12.5","0","24176","0","0","3.26171875","14.078125","10","192","0","0" +"3","block","132","2026-09-29T02:14:28.7949297Z","132.0135008","1.00212260000001","8.203125","0","24176","0","0","3.26171875","14.078125","10","192","0","0" +"3","block","133","2026-09-29T02:14:29.7929164Z","133.0114657","0.997964899999999","13.28125","0","24176","0","0","3.26171875","14.078125","10","192","0","0" +"3","block","134","2026-09-29T02:14:30.7951213Z","134.0133741","1.00190839999999","9.9609375","0","24176","0","0","3.26171875","14.078125","10","192","0","0" +"3","block","135","2026-09-29T02:14:31.7907161Z","135.0094878","0.996113699999995","8.34990059642147","0","24176","0.015625","0.196074504346242","3.26171875","14.078125","10","192","0","0" +"3","block","136","2026-09-29T02:14:32.7876939Z","136.0063784","0.9968906","5.45808966861598","0.195921698930655","24176","0","0","3.26171875","14.078125","10","192","0","0" +"3","block","137","2026-09-29T02:14:33.7878330Z","137.0065832","1.00020480000001","9.1796875","0","24176","0","0","3.26171875","14.078125","10","192","0","0" +"3","block","138","2026-09-29T02:14:34.7896726Z","138.0082971","1.0017139","4.8828125","0","24176","0","0","3.26171875","14.078125","10","192","0","0" +"3","block","139","2026-09-29T02:14:35.7939881Z","139.0126429","1.00434580000001","9.42307692307692","0.194467383644157","24176","0","0","3.26171875","14.078125","10","192","0","0" +"3","block","140","2026-09-29T02:14:36.7944951Z","140.0130913","1.00044839999998","6.0546875","0","24176","0","0","3.26171875","14.078125","10","192","0","0" +"3","block","141","2026-09-29T02:14:37.7918663Z","141.0106432","0.997551900000019","11.5079365079365","0","24176","0","0","3.26171875","14.078125","10","192","0","0" +"3","block","142","2026-09-29T02:14:38.7935677Z","142.0122618","1.0016186","10.5769230769231","0.194996878053183","24176","0","0","3.26171875","14.078125","10","192","0","0" +"3","block","143","2026-09-29T02:14:39.7927036Z","143.011429","0.999167199999988","9.5703125","0","24176","0","0","3.26171875","14.078125","10","192","0","0" +"3","block","144","2026-09-29T02:14:40.7911957Z","144.00988","0.998450999999989","9.12698412698413","0","24176","0","0","3.26171875","14.078125","10","192","0","0" +"3","block","145","2026-09-29T02:14:41.7925847Z","145.0113929","1.00151290000002","16.1538461538462","0.195017458087655","24176","0","0","3.26171875","14.078125","10","192","0","0" +"3","block","146","2026-09-29T02:14:42.7933440Z","146.0120288","1.00063589999999","11.71875","0","24176","0","0","3.26171875","14.078125","10","192","0","0" +"3","block","147","2026-09-29T02:14:43.7975161Z","147.0160956","1.0040668","5.859375","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","148","2026-09-29T02:14:44.7851804Z","148.0037085","0.987612899999988","5.56660039761431","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","149","2026-09-29T02:14:45.8008948Z","149.0194866","1.01577810000001","7.2936660268714","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","150","2026-09-29T02:14:46.7896455Z","150.0082044","0.988717799999989","4.96031746031746","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","151","2026-09-29T02:14:47.7919206Z","151.0107349","1.00253050000001","7.6171875","0.194819509231888","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","152","2026-09-29T02:14:48.7942378Z","152.012881","1.0021461","15.1923076923077","0.19489423747695","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","153","2026-09-29T02:14:49.7844854Z","153.0031538","0.990272800000014","11.1111111111111","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","154","2026-09-29T02:14:50.7844129Z","154.0029556","0.9998018","11.1328125","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","155","2026-09-29T02:14:51.8004225Z","155.0190593","1.01610369999997","6.34615384615385","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","156","2026-09-29T02:14:52.7878031Z","156.0064512","0.987391900000006","8.53174603174603","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","157","2026-09-29T02:14:53.7883217Z","157.007049","1.00059780000001","6.640625","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","158","2026-09-29T02:14:54.7947627Z","158.0136623","1.0066133","11.9230769230769","0.194029325859295","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","159","2026-09-29T02:14:55.7845241Z","159.0031022","0.989439900000008","7.93650793650794","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","160","2026-09-29T02:14:56.7841958Z","160.0029048","0.999802599999981","5.2734375","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","161","2026-09-29T02:14:57.7918363Z","161.0104315","1.0075267","9.1796875","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","162","2026-09-29T02:14:58.7956010Z","162.0142831","1.00385160000002","11.1538461538462","0.194563120684368","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","163","2026-09-29T02:14:59.7811208Z","162.9998351","0.985551999999984","8.92857142857143","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","164","2026-09-29T02:15:00.7956664Z","164.0144057","1.01457060000001","5.19230769230769","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","165","2026-09-29T02:15:01.7923565Z","165.0110933","0.996687600000001","13.28125","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","166","2026-09-29T02:15:02.7990654Z","166.0176577","1.0065644","7.2265625","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","167","2026-09-29T02:15:03.7854848Z","167.0043261","0.986668399999985","5.15873015873016","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","168","2026-09-29T02:15:04.7993335Z","168.0180254","1.01369930000001","5.19230769230769","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","169","2026-09-29T02:15:05.7809082Z","168.9995351","0.981509700000004","6.94444444444444","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","170","2026-09-29T02:15:06.7939765Z","170.0125046","1.0129695","7.30769230769231","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","171","2026-09-29T02:15:07.7917189Z","171.0104238","0.997919199999984","21.2301587301587","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","172","2026-09-29T02:15:08.7900586Z","172.008837","0.998413200000016","11.71875","0.195622914440631","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","173","2026-09-29T02:15:09.7941986Z","173.0123483","1.00351129999999","17.1153846153846","0.194629098845228","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","174","2026-09-29T02:15:10.7918575Z","174.0104679","0.998119599999995","11.1111111111111","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","175","2026-09-29T02:15:11.7934279Z","175.0116299","1.00116200000002","11.9230769230769","0.195085810288441","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","176","2026-09-29T02:15:12.7916171Z","176.0101387","0.998508799999996","12.6984126984127","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","177","2026-09-29T02:15:13.7902380Z","177.0088368","0.998698099999984","8.3984375","0.195567108818974","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","178","2026-09-29T02:15:14.7901487Z","178.0087431","0.999906300000021","11.328125","0.19533080249619","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","179","2026-09-29T02:15:15.7892656Z","179.00796","0.999216899999993","7.03125","0.195465569087153","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","180","2026-09-29T02:15:16.8009690Z","180.0196197","1.0116597","5.38461538461539","0.193061461279915","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","181","2026-09-29T02:15:17.7997470Z","181.0183164","0.998696700000011","5.859375","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","182","2026-09-29T02:15:18.7987019Z","182.0174861","0.999169699999982","6.4453125","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","183","2026-09-29T02:15:19.7834123Z","183.0016157","0.984129600000017","11.3095238095238","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","184","2026-09-29T02:15:20.7846964Z","184.0033206","1.00170489999999","6.0546875","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","185","2026-09-29T02:15:21.7876182Z","185.0064025","1.00308189999998","11.71875","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","186","2026-09-29T02:15:22.8027070Z","186.0213984","1.0149959","5","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","187","2026-09-29T02:15:23.7899193Z","187.0084442","0.987045800000004","6.74603174603175","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","188","2026-09-29T02:15:24.7901477Z","188.008762","1.0003178","4.1015625","0.195250449407177","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","189","2026-09-29T02:15:25.7961097Z","189.0149977","1.00623569999999","13.2692307692308","0.194102137302425","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","190","2026-09-29T02:15:26.7814473Z","189.9999898","0.984992099999999","7.34126984126984","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","191","2026-09-29T02:15:27.7861101Z","191.0048144","1.00482460000001","5.2734375","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","192","2026-09-29T02:15:28.7897345Z","192.0084472","1.00363279999999","5.46875","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","193","2026-09-29T02:15:29.7895477Z","193.0082369","0.999789700000008","10.15625","0.195353582858474","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","194","2026-09-29T02:15:30.8006585Z","194.0192813","1.0110444","6.34615384615385","0.193178954356504","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","195","2026-09-29T02:15:31.7974213Z","195.0157702","0.996488900000003","4.296875","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","196","2026-09-29T02:15:32.7953263Z","196.0140968","0.998326600000013","6.25","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","197","2026-09-29T02:15:33.7823122Z","197.0010594","0.986962599999998","6.94444444444444","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","198","2026-09-29T02:15:34.7931339Z","198.0117812","1.0107218","10.1923076923077","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","199","2026-09-29T02:15:35.7916933Z","199.0103806","0.998599399999989","8.73015873015873","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","200","2026-09-29T02:15:36.7875669Z","200.0062935","0.995912900000008","9.1796875","0.196114037683414","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","201","2026-09-29T02:15:37.7873262Z","201.0060159","0.999722399999996","7.2265625","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","202","2026-09-29T02:15:38.7885679Z","202.0071758","1.0011599","6.4453125","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","203","2026-09-29T02:15:39.8021074Z","203.0203305","1.0131547","8.46153846153846","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","204","2026-09-29T02:15:40.7993046Z","204.0175187","0.997188199999982","6.25","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","205","2026-09-29T02:15:41.7817075Z","205.00037","0.982851300000021","7.93650793650794","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","206","2026-09-29T02:15:42.7836628Z","206.00237","1.00199999999998","5.078125","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","207","2026-09-29T02:15:43.7843143Z","207.0030186","1.00064860000001","5.2734375","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","208","2026-09-29T02:15:44.8002284Z","208.0188481","1.0158295","5.19230769230769","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","209","2026-09-29T02:15:45.7840843Z","209.0027849","0.983936800000009","7.73809523809524","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","210","2026-09-29T02:15:46.7831114Z","210.0017003","0.998915399999987","5.6640625","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","211","2026-09-29T02:15:47.7842308Z","211.0029609","1.00126059999999","6.0546875","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","212","2026-09-29T02:15:48.7998297Z","212.0185896","1.01562870000001","5.38461538461539","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","213","2026-09-29T02:15:49.7983347Z","213.0169436","0.998354000000006","11.328125","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","214","2026-09-29T02:15:50.7990244Z","214.0177321","1.0007885","12.3046875","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","215","2026-09-29T02:15:51.7928670Z","215.011465","0.993732899999998","8.59375","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","216","2026-09-29T02:15:52.7999353Z","216.0186937","1.00722870000001","7.03125","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","217","2026-09-29T02:15:53.8015687Z","217.0204296","1.0017359","9.5703125","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","218","2026-09-29T02:15:54.7983385Z","218.0170084","0.99657879999998","8.984375","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","219","2026-09-29T02:15:55.7832149Z","219.0017782","0.984769800000009","4.56349206349206","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","220","2026-09-29T02:15:56.7855190Z","220.0041671","1.0023889","5.6640625","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","221","2026-09-29T02:15:57.7891278Z","221.0074325","1.0032654","7.421875","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","222","2026-09-29T02:15:58.8004890Z","222.0191864","1.0117539","5.38461538461539","0.19304348616793","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","223","2026-09-29T02:15:59.7881341Z","223.0068701","0.987683699999991","6.54761904761905","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","224","2026-09-29T02:16:00.7897011Z","224.008332","1.00146190000001","9.9609375","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","225","2026-09-29T02:16:01.7900487Z","225.0087644","1.00043239999999","13.4765625","0.195228083376749","24176","0.015625","0.195228083376749","3.26171875","14.078125","10","194","0","0" +"3","block","226","2026-09-29T02:16:02.7880917Z","226.0067595","0.997995099999997","6.25","0.19570486869124","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","227","2026-09-29T02:16:03.7903618Z","227.0090946","1.00233510000001","5.2734375","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","228","2026-09-29T02:16:04.7977340Z","228.0158813","1.00678669999999","8.46153846153846","0.193995907971372","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","229","2026-09-29T02:16:05.7840058Z","229.0028494","0.986968100000013","8.13492063492063","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","230","2026-09-29T02:16:06.7987886Z","230.0175631","1.01471369999999","5","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","231","2026-09-29T02:16:07.7877081Z","231.0063483","0.988785199999995","2.57936507936508","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","232","2026-09-29T02:16:08.7910214Z","232.0096105","1.00326219999999","7.03125","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","233","2026-09-29T02:16:09.7922339Z","233.0109302","1.00131970000001","13.4874759152216","0.195055085803263","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","234","2026-09-29T02:16:10.7953410Z","234.0140473","1.0031171","14.6198830409357","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","235","2026-09-29T02:16:11.7817437Z","235.0003812","0.986333900000005","4.96031746031746","0","24176","0","0","3.26171875","14.078125","10","194","0","0" +"3","block","236","2026-09-29T02:16:12.7942734Z","236.0129757","1.01259450000001","5.38461538461539","0","24176","0","0","3.23046875","14.0625","9","192","0","0" +"3","block","237","2026-09-29T02:16:13.7902349Z","237.0088574","0.995881699999984","12.1031746031746","0","24176","0","0","3.2578125","14.078125","10","192","0","0" +"3","block","238","2026-09-29T02:16:14.7914295Z","238.010162","1.0013046","10.9375","0.195058027297588","24176","0","0","3.2578125","14.078125","10","192","0","0" +"3","block","239","2026-09-29T02:16:15.7897819Z","239.0086069","0.99844490000001","7.421875","0.195616703535666","24176","0","0","3.2578125","14.078125","10","192","0","0" +"3","block","240","2026-09-29T02:16:16.8015447Z","240.0201028","1.0114959","5.76923076923077","0.193092725338778","24176","0","0","3.2578125","14.078125","10","192","0","0" +"3","block","241","2026-09-29T02:16:17.7897317Z","241.0086527","0.98854990000001","7.14285714285714","0","24176","0","0","3.2578125","14.078125","10","192","0","0" +"3","block","242","2026-09-29T02:16:18.7936622Z","242.0123225","1.00366979999998","8.07692307692308","0.194598362927731","24176","0","0","3.2578125","14.078125","10","192","0","0" +"3","block","243","2026-09-29T02:16:19.7942630Z","243.0131337","1.00081120000002","7.8125","0","24176","0","0","3.2578125","14.078125","10","192","0","0" +"3","block","244","2026-09-29T02:16:20.7962253Z","244.0149685","1.00183479999998","10.15625","0","24176","0","0","3.2578125","14.078125","10","192","0","0" +"3","block","245","2026-09-29T02:16:21.7889554Z","245.0079976","0.993029100000001","7.73809523809524","0","24176","0","0","3.2578125","14.078125","10","192","0","0" +"3","block","246","2026-09-29T02:16:22.7893693Z","246.0080269","1.00002929999999","6.25","0","24176","0","0","3.2578125","14.078125","10","192","0","0" +"3","block","247","2026-09-29T02:16:23.7919738Z","247.0106868","1.00265990000003","9.1796875","0.194794366464636","24176","0","0","3.2578125","14.078125","10","192","0","0" +"3","block","248","2026-09-29T02:16:24.7927444Z","248.0113225","1.00063569999998","7.88461538461539","0","24176","0","0","3.2578125","14.078125","10","192","0","0" +"3","block","249","2026-09-29T02:16:25.7932585Z","249.0119821","1.00065960000001","8.984375","0","24176","0","0","3.2578125","14.078125","10","192","0","0" +"3","block","250","2026-09-29T02:16:26.7836245Z","250.0022977","0.990315600000002","6.34920634920635","0","24176","0","0","3.2578125","14.078125","10","192","0","0" +"3","block","251","2026-09-29T02:16:27.7870628Z","251.0056765","1.00337880000001","8.59375","0","24176","0","0","3.2578125","14.078125","10","192","0","0" +"3","block","252","2026-09-29T02:16:28.7886504Z","252.007306","1.00162950000001","4.6875","0","24176","0","0","3.2578125","14.078125","10","192","0","0" +"3","block","253","2026-09-29T02:16:29.7874424Z","253.0066687","0.999362699999978","7.03125","0.195437052033265","24176","0","0","3.2578125","14.078125","10","192","0","0" +"3","block","254","2026-09-29T02:16:30.7971266Z","254.0158841","1.00921540000002","9.42307692307692","0","24176","0","0","3.2578125","14.078125","10","192","0","0" +"3","block","255","2026-09-29T02:16:31.7953730Z","255.0140412","0.998157099999986","6.640625","0","24176","0","0","3.2578125","14.078125","10","192","0","0" +"3","block","256","2026-09-29T02:16:32.7912915Z","256.0099094","0.99586819999999","27.3809523809524","0","24176","0.015625","0.196122840351767","3.2578125","14.078125","10","192","0","0" +"3","block","257","2026-09-29T02:16:33.7998573Z","257.0187366","1.00882720000004","27.3076923076923","0.193603522981926","24176","0","0","3.2578125","14.078125","10","192","0","0" +"3","block","258","2026-09-29T02:16:34.8000404Z","258.0186974","0.999960799999997","14.84375","0","24176","0","0","3.2578125","14.078125","10","192","0","0" +"3","block","259","2026-09-29T02:16:35.7986596Z","259.0172426","0.998545199999967","6.0546875","0","24176","0","0","3.2578125","14.078125","10","192","0","0" +"3","block","260","2026-09-29T02:16:36.7833693Z","260.0020281","0.984785499999987","5.57768924302789","0","24176","0","0","3.2578125","14.078125","10","192","0","0" +"3","block","261","2026-09-29T02:16:37.7821003Z","261.0008583","0.998830200000043","7.78210116731517","0","24176","0","0","3.2578125","14.078125","10","192","0","0" +"3","block","262","2026-09-29T02:16:38.7959371Z","262.0145634","1.01370509999998","5.96153846153846","0","24176","0","0","3.2578125","14.078125","10","192","0","0" +"3","block","263","2026-09-29T02:16:39.7816251Z","263.0003229","0.985759500000029","5.56660039761431","0","24176","0","0","3.2578125","14.078125","10","192","0","0" +"3","block","264","2026-09-29T02:16:40.7822295Z","264.0009199","1.00059699999997","7.60233918128655","0","24176","0","0","3.2578125","14.078125","10","192","0","0" +"3","block","265","2026-09-29T02:16:41.7851275Z","265.0035883","1.0026684","8.7890625","0","24176","0","0","3.2578125","14.078125","10","192","0","0" +"3","block","266","2026-09-29T02:16:42.7896167Z","266.0081869","1.00459860000001","4.4921875","0","24176","0","0","3.2578125","14.078125","10","192","0","0" +"3","block","267","2026-09-29T02:16:43.7878243Z","267.0063336","0.998146700000007","5.859375","0.195675144745756","24176","0","0","3.3515625","14.27734375","13","194","0","0" +"3","block","268","2026-09-29T02:16:44.7973145Z","268.0158946","1.00956099999996","6.53846153846154","0","24176","0","0","3.3515625","14.27734375","13","194","0","0" +"3","block","269","2026-09-29T02:16:45.7821217Z","269.000914","0.985019399999999","6.34920634920635","0","24176","0","0","3.3515625","14.27734375","13","194","0","0" +"3","block","270","2026-09-29T02:16:46.7962370Z","270.0147804","1.01386640000004","5.19230769230769","0","24176","0","0","3.3515625","14.27734375","13","194","0","0" +"3","block","271","2026-09-29T02:16:47.7828285Z","271.0010232","0.986242799999957","4.96031746031746","0","24176","0","0","3.3515625","14.27734375","13","194","0","0" +"3","block","272","2026-09-29T02:16:48.7837340Z","272.0023625","1.00133930000004","4.1015625","0","24176","0","0","3.3515625","14.27734375","13","194","0","0" +"3","block","273","2026-09-29T02:16:49.7863416Z","273.0049589","1.00259639999996","6.640625","0","24176","0","0","3.3515625","14.27734375","13","194","0","0" +"3","block","274","2026-09-29T02:16:50.8023935Z","274.0211568","1.01619790000001","10.5769230769231","0","24176","0","0","3.3515625","14.27734375","13","194","0","0" +"3","block","275","2026-09-29T02:16:51.7946657Z","275.0133571","0.992200300000036","8.7890625","0","24176","0","0","3.3515625","14.27734375","13","194","0","0" +"3","block","276","2026-09-29T02:16:52.7964233Z","276.0150725","1.00171539999997","9.5703125","0","24176","0","0","3.3515625","14.27734375","13","194","0","0" +"3","block","277","2026-09-29T02:16:53.7841115Z","277.0026154","0.987542899999994","7.53968253968254","0","24176","0","0","3.3515625","14.27734375","13","194","0","0" +"3","block","278","2026-09-29T02:16:54.7972928Z","278.0159282","1.01331279999999","7.11538461538461","0","24176","0","0","3.3515625","14.27734375","13","194","0","0" +"3","block","279","2026-09-29T02:16:55.7860505Z","279.0046037","0.988675499999999","5.15873015873016","0","24176","0","0","3.3515625","14.27734375","13","194","0","0" +"3","block","280","2026-09-29T02:16:56.7878160Z","280.0066311","1.00202740000003","6.640625","0","24176","0","0","3.3515625","14.27734375","13","194","0","0" +"3","block","281","2026-09-29T02:16:57.7889925Z","281.0075574","1.0009263","7.2265625","0","24176","0","0","3.3515625","14.27734375","13","194","0","0" +"3","block","282","2026-09-29T02:16:58.7976146Z","282.0162148","1.0086574","5","0","24176","0","0","3.3515625","14.27734375","13","194","0","0" +"3","block","283","2026-09-29T02:16:59.7823717Z","283.0010726","0.984857799999986","5.95238095238095","0","24176","0","0","3.3515625","14.27734375","13","194","0","0" +"3","block","284","2026-09-29T02:17:00.7821474Z","284.0007803","0.999707699999988","7.8125","0","24176","0","0","3.3515625","14.27734375","13","194","0","0" +"3","block","285","2026-09-29T02:17:01.7847901Z","285.0034083","1.00262800000002","6.0546875","0","24176","0","0","3.3515625","14.27734375","13","194","0","0" +"3","block","286","2026-09-29T02:17:02.7992656Z","286.0180374","1.01462909999998","5.76923076923077","0","24176","0","0","3.3515625","14.27734375","13","194","0","0" +"3","block","287","2026-09-29T02:17:03.8016458Z","287.0202899","1.0022525","5.6640625","0","24176","0","0","3.3515625","14.27734375","13","194","0","0" +"3","block","288","2026-09-29T02:17:04.7893421Z","288.0080345","0.987744600000042","8.53174603174603","0","24176","0","0","3.3515625","14.27734375","13","194","0","0" +"3","block","289","2026-09-29T02:17:05.7876492Z","289.0064433","0.998408799999993","6.8359375","0.195623776553253","24176","0","0","3.3515625","14.27734375","13","194","0","0" +"3","block","290","2026-09-29T02:17:06.7896987Z","290.0083357","1.00189239999997","7.2265625","0","24176","0","0","3.3515625","14.27734375","13","194","0","0" +"3","block","291","2026-09-29T02:17:07.7980562Z","291.0165976","1.00826190000004","5.76923076923077","0.193712070246821","24176","0","0","3.3515625","14.27734375","13","194","0","0" +"3","block","292","2026-09-29T02:17:08.7967272Z","292.0154898","0.9988922","5.2734375","0","24176","0","0","3.3515625","14.27734375","13","194","0","0" +"3","block","293","2026-09-29T02:17:09.7903094Z","293.0089158","0.993425999999999","10.3174603174603","0","24176","0","0","3.3515625","14.27734375","13","194","0","0" +"3","block","294","2026-09-29T02:17:10.7965315Z","294.0152847","1.00636889999998","11.7307692307692","0.194076446519763","24176","0","0","3.3515625","14.27734375","13","194","0","0" +"3","block","295","2026-09-29T02:17:11.7821940Z","295.0007551","0.985470399999997","7.14285714285714","0","24176","0","0","3.3515625","14.27734375","13","194","0","0" +"3","block","296","2026-09-29T02:17:12.7882686Z","296.0069179","1.00616279999997","4.4921875","0","24176","0","0","3.3515625","14.27734375","13","194","0","0" +"3","block","297","2026-09-29T02:17:13.7880793Z","297.0067217","0.999803800000052","7.2265625","0","24176","0","0","3.3515625","14.27734375","13","194","0","0" +"3","block","298","2026-09-29T02:17:14.7875152Z","298.0061411","0.999419399999965","8.203125","0","24176","0","0","3.3515625","14.27734375","13","194","0","0" +"3","block","299","2026-09-29T02:17:15.7987571Z","299.017375","1.01123389999998","3.84615384615385","0","24176","0","0","3.3515625","14.27734375","13","194","0","0" +"3","block","300","2026-09-29T02:17:16.7845235Z","300.0032511","0.985876100000041","8.13492063492063","0","24176","0","0","3.3515625","14.27734375","13","194","0","0" diff --git a/docs/benchmarks/2026-09-29-v0.2.0/results/status.txt b/docs/benchmarks/2026-09-29-v0.2.0/results/status.txt new file mode 100644 index 0000000..ab330d9 --- /dev/null +++ b/docs/benchmarks/2026-09-29-v0.2.0/results/status.txt @@ -0,0 +1 @@ +SUCCESS diff --git a/docs/benchmarks/2026-09-29-v0.2.0/results/update-layer-evidence.jsonl b/docs/benchmarks/2026-09-29-v0.2.0/results/update-layer-evidence.jsonl new file mode 100644 index 0000000..a82ff29 --- /dev/null +++ b/docs/benchmarks/2026-09-29-v0.2.0/results/update-layer-evidence.jsonl @@ -0,0 +1,3 @@ +{"timestamp":"2026-09-29T01:49:36.968Z","event":"update_monitoring_started","source":"update_orchestrator","action":"Layer 3 skipped because the scheduled task \\Microsoft\\Windows\\UpdateOrchestrator\\Reboot does not exist on this machine.","success":false} +{"timestamp":"2026-09-29T02:00:43.263Z","event":"update_monitoring_started","source":"update_orchestrator","action":"Layer 3 skipped because the scheduled task \\Microsoft\\Windows\\UpdateOrchestrator\\Reboot does not exist on this machine.","success":false} +{"timestamp":"2026-09-29T02:11:46.743Z","event":"update_monitoring_started","source":"update_orchestrator","action":"Layer 3 skipped because the scheduled task \\Microsoft\\Windows\\UpdateOrchestrator\\Reboot does not exist on this machine.","success":false} diff --git a/docs/benchmarks/2026-09-29-v0.2.0/summary.csv b/docs/benchmarks/2026-09-29-v0.2.0/summary.csv new file mode 100644 index 0000000..656ab7b --- /dev/null +++ b/docs/benchmarks/2026-09-29-v0.2.0/summary.csv @@ -0,0 +1,93 @@ +scope,scenario,metric,n,mean,median,p95,maximum,minimum +pooled,baseline,system_cpu_pct,900,9.310717623187722,7.8125,17.1153846153846,86.9230769230769,3.515625 +pooled,baseline,collector_cpu_pct,900,0.013671551857681225,0.0,0.194617520833106,1.37095722107078,0.0 +pooled,baseline,interval_s,900,1.0000236361111112,1.00029585000002,1.01091550000001,1.0194018,0.980372000000003 +pooled,allow,cpu_pct,900,0.0023870911123183057,0.0,0.0,0.19714806679066,0.0 +pooled,allow,private_mib,900,1.9550390625,1.95703125,2.01953125,2.04296875,1.90625 +pooled,allow,working_set_mib,900,12.56752170138889,12.5625,12.71484375,12.75390625,12.4921875 +pooled,allow,threads,900,5.985555555555556,5.0,8.0,8.0,5.0 +pooled,allow,handles,900,174.67111111111112,174.0,176.0,176.0,174.0 +pooled,allow,io_read_bytes_s,900,0.0,0.0,0.0,0.0,0.0 +pooled,allow,io_write_bytes_s,900,0.0,0.0,0.0,0.0,0.0 +pooled,allow,system_cpu_pct,900,9.140272307001851,7.715201465201465,18.359375,46.09375,3.515625 +pooled,allow,collector_cpu_pct,900,0.049477888509870324,0.0,0.195845552424593,0.393158948740528,0.0 +pooled,allow,interval_s,900,1.000025832888889,0.9999746000000015,1.01361110000001,1.0248538,0.9665964 +pooled,block,cpu_pct,900,0.004991235622742559,0.0,0.0,0.197048179181501,0.0 +pooled,block,private_mib,900,3.251236979166667,3.24609375,3.3359375,3.359375,3.19140625 +pooled,block,working_set_mib,900,13.946493055555555,14.046875,14.265625,14.27734375,13.0234375 +pooled,block,threads,900,10.333333333333334,10.0,13.0,13.0,9.0 +pooled,block,handles,900,193.48888888888888,194.0,194.0,194.0,192.0 +pooled,block,io_read_bytes_s,900,0.0,0.0,0.0,0.0,0.0 +pooled,block,io_write_bytes_s,900,0.0,0.0,0.0,0.0,0.0 +pooled,block,system_cpu_pct,900,9.026946098286222,7.421875,18.0555555555556,45.7692307692308,2.57936507936508 +pooled,block,collector_cpu_pct,900,0.040146895226407533,0.0,0.195518694014706,0.389974016374465,0.0 +pooled,block,interval_s,900,1.0000167937777777,1.00011315,1.01275580000001,1.0183159,0.979117800000012 +1,baseline,system_cpu_pct,300,9.4270893620663,8.169022817460315,17.6923076923077,40.2777777777778,3.76984126984127 +1,baseline,collector_cpu_pct,300,0.020181581221190112,0.0,0.195230678822508,1.37095722107078,0.0 +1,baseline,interval_s,300,1.0000352026666668,1.0002395000000002,1.0089906,1.0150003,0.9812941 +1,allow,cpu_pct,300,0.0026040354623398957,0.0,0.0,0.196429792660654,0.0 +1,allow,private_mib,300,1.943046875,1.9140625,2.0,2.0,1.9140625 +1,allow,working_set_mib,300,12.527005208333334,12.4921875,12.66796875,12.66796875,12.4921875 +1,allow,threads,300,5.98,5.0,8.0,8.0,5.0 +1,allow,handles,300,174.66666666666666,174.0,176.0,176.0,174.0 +1,allow,io_read_bytes_s,300,0.0,0.0,0.0,0.0,0.0 +1,allow,io_write_bytes_s,300,0.0,0.0,0.0,0.0,0.0 +1,allow,system_cpu_pct,300,9.331818218824207,7.480778769841271,19.7265625,41.2109375,3.515625 +1,allow,collector_cpu_pct,300,0.042966585128608285,0.0,0.195573414492276,0.393158948740528,0.0 +1,allow,interval_s,300,1.000050385,0.999954199999996,1.01377969999999,1.0248538,0.9665964 +1,block,cpu_pct,300,0.003906120846718285,0.0,0.0,0.195852151016912,0.0 +1,block,private_mib,300,3.2445703125,3.234375,3.3359375,3.359375,3.203125 +1,block,working_set_mib,300,14.0693359375,14.046875,14.265625,14.26953125,13.92578125 +1,block,threads,300,10.366666666666667,10.0,13.0,13.0,9.0 +1,block,handles,300,193.48,194.0,194.0,194.0,192.0 +1,block,io_read_bytes_s,300,0.0,0.0,0.0,0.0,0.0 +1,block,io_write_bytes_s,300,0.0,0.0,0.0,0.0,0.0 +1,block,system_cpu_pct,300,8.863378133805679,7.324481074481075,17.7734375,45.7692307692308,3.90625 +1,block,collector_cpu_pct,300,0.02929590635038713,0.0,0.19531873066751,0.196246831163168,0.0 +1,block,interval_s,300,1.0000330643333333,1.000224900000005,1.01188690000004,1.0183159,0.979117800000012 +2,baseline,system_cpu_pct,300,9.093366993306686,7.97216021825397,15.0793650793651,41.6666666666667,4.296875 +2,baseline,collector_cpu_pct,300,0.015624672579777866,0.0,0.194995787841381,0.390417844291822,0.0 +2,baseline,interval_s,300,1.0000209553333335,1.00044265,1.0106092,1.0194018,0.981294300000002 +2,allow,cpu_pct,300,0.0032551589579972104,0.0,0.0,0.19714806679066,0.0 +2,allow,private_mib,300,1.9863020833333334,1.95703125,2.04296875,2.04296875,1.95703125 +2,allow,working_set_mib,300,12.60734375,12.578125,12.75390625,12.75390625,12.578125 +2,allow,threads,300,5.99,5.0,8.0,8.0,5.0 +2,allow,handles,300,174.67333333333335,174.0,176.0,176.0,174.0 +2,allow,io_read_bytes_s,300,0.0,0.0,0.0,0.0,0.0 +2,allow,io_write_bytes_s,300,0.0,0.0,0.0,0.0,0.0 +2,allow,system_cpu_pct,300,9.528057373402646,8.357699805068226,20.0392927308448,46.09375,3.96825396825397 +2,allow,collector_cpu_pct,300,0.05273357511955474,0.0,0.195630438588794,0.197271146027562,0.0 +2,allow,interval_s,300,1.0000151683333331,0.999932800000009,1.01079730000001,1.01451029999998,0.985216799999996 +2,block,cpu_pct,300,0.008463486718889725,0.0,0.0,0.197048179181501,0.0 +2,block,private_mib,300,3.2369270833333332,3.244140625,3.2890625,3.2890625,3.19140625 +2,block,working_set_mib,300,13.669856770833333,14.01953125,14.0390625,14.04296875,13.0234375 +2,block,threads,300,10.26,10.0,12.0,12.0,9.0 +2,block,handles,300,193.49333333333334,194.0,194.0,194.0,192.0 +2,block,io_read_bytes_s,300,0.0,0.0,0.0,0.0,0.0 +2,block,io_write_bytes_s,300,0.0,0.0,0.0,0.0,0.0 +2,block,system_cpu_pct,300,9.668837806705387,8.0078125,19.53125,41.468253968254,3.515625 +2,block,collector_cpu_pct,300,0.0546871449528259,0.0,0.195682437648375,0.389974016374465,0.0 +2,block,interval_s,300,1.0000064923333332,1.0000182,1.01092840000001,1.015154,0.98461309999999 +3,baseline,system_cpu_pct,300,9.41169548279246,7.4609375,20.1171875,86.9230769230769,3.515625 +3,baseline,collector_cpu_pct,300,0.0052082565098137265,0.0,0.0,0.196882381041712,0.0 +3,baseline,interval_s,300,1.0000147503333332,1.0003615499999952,1.0121634,1.01521399999996,0.980372000000003 +3,allow,cpu_pct,300,0.0013020677796996792,0.0,0.0,0.194801185847389,0.0 +3,allow,private_mib,300,1.9357682291666667,1.90625,1.99609375,1.99609375,1.90625 +3,allow,working_set_mib,300,12.568216145833333,12.5390625,12.71484375,12.71484375,12.5390625 +3,allow,threads,300,5.986666666666666,5.0,8.0,8.0,5.0 +3,allow,handles,300,174.67333333333335,174.0,176.0,176.0,174.0 +3,allow,io_read_bytes_s,300,0.0,0.0,0.0,0.0,0.0 +3,allow,io_write_bytes_s,300,0.0,0.0,0.0,0.0,0.0 +3,allow,system_cpu_pct,300,8.56093271608931,7.421875,15.625,37.109375,3.90625 +3,allow,collector_cpu_pct,300,0.052733745077837124,0.0,0.197443288222162,0.39194420580791,0.0 +3,allow,interval_s,300,1.0000119453333334,1.000105950000005,1.01430719999999,1.0191368,0.978705200000036 +3,block,cpu_pct,300,0.002604138477735689,0.0,0.0,0.196122840351767,0.0 +3,block,private_mib,300,3.2722135416666664,3.26171875,3.3515625,3.3515625,3.23046875 +3,block,working_set_mib,300,14.100286458333333,14.078125,14.27734375,14.27734375,14.0625 +3,block,threads,300,10.373333333333333,10.0,13.0,13.0,9.0 +3,block,handles,300,193.49333333333334,194.0,194.0,194.0,192.0 +3,block,io_read_bytes_s,300,0.0,0.0,0.0,0.0,0.0 +3,block,io_write_bytes_s,300,0.0,0.0,0.0,0.0,0.0 +3,block,system_cpu_pct,300,8.548628772863976,7.2601142634357,15.1923076923077,39.2857142857143,2.57936507936508 +3,block,collector_cpu_pct,300,0.03645793868829966,0.0,0.195363626661097,0.197120668177123,0.0 +3,block,interval_s,300,1.0000108246666666,1.0001475499999999,1.0142349,1.01716189999999,0.981509700000004 diff --git a/docs/benchmarks/2026-09-29-v0.2.0/summary.json b/docs/benchmarks/2026-09-29-v0.2.0/summary.json new file mode 100644 index 0000000..91bd9bd --- /dev/null +++ b/docs/benchmarks/2026-09-29-v0.2.0/summary.json @@ -0,0 +1,1014 @@ +[ + { + "scope": "pooled", + "scenario": "baseline", + "metric": "system_cpu_pct", + "n": 900, + "mean": 9.310717623187722, + "median": 7.8125, + "p95": 17.1153846153846, + "maximum": 86.9230769230769, + "minimum": 3.515625 + }, + { + "scope": "pooled", + "scenario": "baseline", + "metric": "collector_cpu_pct", + "n": 900, + "mean": 0.013671551857681225, + "median": 0.0, + "p95": 0.194617520833106, + "maximum": 1.37095722107078, + "minimum": 0.0 + }, + { + "scope": "pooled", + "scenario": "baseline", + "metric": "interval_s", + "n": 900, + "mean": 1.0000236361111112, + "median": 1.00029585000002, + "p95": 1.01091550000001, + "maximum": 1.0194018, + "minimum": 0.980372000000003 + }, + { + "scope": "pooled", + "scenario": "allow", + "metric": "cpu_pct", + "n": 900, + "mean": 0.0023870911123183057, + "median": 0.0, + "p95": 0.0, + "maximum": 0.19714806679066, + "minimum": 0.0 + }, + { + "scope": "pooled", + "scenario": "allow", + "metric": "private_mib", + "n": 900, + "mean": 1.9550390625, + "median": 1.95703125, + "p95": 2.01953125, + "maximum": 2.04296875, + "minimum": 1.90625 + }, + { + "scope": "pooled", + "scenario": "allow", + "metric": "working_set_mib", + "n": 900, + "mean": 12.56752170138889, + "median": 12.5625, + "p95": 12.71484375, + "maximum": 12.75390625, + "minimum": 12.4921875 + }, + { + "scope": "pooled", + "scenario": "allow", + "metric": "threads", + "n": 900, + "mean": 5.985555555555556, + "median": 5.0, + "p95": 8.0, + "maximum": 8.0, + "minimum": 5.0 + }, + { + "scope": "pooled", + "scenario": "allow", + "metric": "handles", + "n": 900, + "mean": 174.67111111111112, + "median": 174.0, + "p95": 176.0, + "maximum": 176.0, + "minimum": 174.0 + }, + { + "scope": "pooled", + "scenario": "allow", + "metric": "io_read_bytes_s", + "n": 900, + "mean": 0.0, + "median": 0.0, + "p95": 0.0, + "maximum": 0.0, + "minimum": 0.0 + }, + { + "scope": "pooled", + "scenario": "allow", + "metric": "io_write_bytes_s", + "n": 900, + "mean": 0.0, + "median": 0.0, + "p95": 0.0, + "maximum": 0.0, + "minimum": 0.0 + }, + { + "scope": "pooled", + "scenario": "allow", + "metric": "system_cpu_pct", + "n": 900, + "mean": 9.140272307001851, + "median": 7.715201465201465, + "p95": 18.359375, + "maximum": 46.09375, + "minimum": 3.515625 + }, + { + "scope": "pooled", + "scenario": "allow", + "metric": "collector_cpu_pct", + "n": 900, + "mean": 0.049477888509870324, + "median": 0.0, + "p95": 0.195845552424593, + "maximum": 0.393158948740528, + "minimum": 0.0 + }, + { + "scope": "pooled", + "scenario": "allow", + "metric": "interval_s", + "n": 900, + "mean": 1.000025832888889, + "median": 0.9999746000000015, + "p95": 1.01361110000001, + "maximum": 1.0248538, + "minimum": 0.9665964 + }, + { + "scope": "pooled", + "scenario": "block", + "metric": "cpu_pct", + "n": 900, + "mean": 0.004991235622742559, + "median": 0.0, + "p95": 0.0, + "maximum": 0.197048179181501, + "minimum": 0.0 + }, + { + "scope": "pooled", + "scenario": "block", + "metric": "private_mib", + "n": 900, + "mean": 3.251236979166667, + "median": 3.24609375, + "p95": 3.3359375, + "maximum": 3.359375, + "minimum": 3.19140625 + }, + { + "scope": "pooled", + "scenario": "block", + "metric": "working_set_mib", + "n": 900, + "mean": 13.946493055555555, + "median": 14.046875, + "p95": 14.265625, + "maximum": 14.27734375, + "minimum": 13.0234375 + }, + { + "scope": "pooled", + "scenario": "block", + "metric": "threads", + "n": 900, + "mean": 10.333333333333334, + "median": 10.0, + "p95": 13.0, + "maximum": 13.0, + "minimum": 9.0 + }, + { + "scope": "pooled", + "scenario": "block", + "metric": "handles", + "n": 900, + "mean": 193.48888888888888, + "median": 194.0, + "p95": 194.0, + "maximum": 194.0, + "minimum": 192.0 + }, + { + "scope": "pooled", + "scenario": "block", + "metric": "io_read_bytes_s", + "n": 900, + "mean": 0.0, + "median": 0.0, + "p95": 0.0, + "maximum": 0.0, + "minimum": 0.0 + }, + { + "scope": "pooled", + "scenario": "block", + "metric": "io_write_bytes_s", + "n": 900, + "mean": 0.0, + "median": 0.0, + "p95": 0.0, + "maximum": 0.0, + "minimum": 0.0 + }, + { + "scope": "pooled", + "scenario": "block", + "metric": "system_cpu_pct", + "n": 900, + "mean": 9.026946098286222, + "median": 7.421875, + "p95": 18.0555555555556, + "maximum": 45.7692307692308, + "minimum": 2.57936507936508 + }, + { + "scope": "pooled", + "scenario": "block", + "metric": "collector_cpu_pct", + "n": 900, + "mean": 0.040146895226407533, + "median": 0.0, + "p95": 0.195518694014706, + "maximum": 0.389974016374465, + "minimum": 0.0 + }, + { + "scope": "pooled", + "scenario": "block", + "metric": "interval_s", + "n": 900, + "mean": 1.0000167937777777, + "median": 1.00011315, + "p95": 1.01275580000001, + "maximum": 1.0183159, + "minimum": 0.979117800000012 + }, + { + "scope": 1, + "scenario": "baseline", + "metric": "system_cpu_pct", + "n": 300, + "mean": 9.4270893620663, + "median": 8.169022817460315, + "p95": 17.6923076923077, + "maximum": 40.2777777777778, + "minimum": 3.76984126984127 + }, + { + "scope": 1, + "scenario": "baseline", + "metric": "collector_cpu_pct", + "n": 300, + "mean": 0.020181581221190112, + "median": 0.0, + "p95": 0.195230678822508, + "maximum": 1.37095722107078, + "minimum": 0.0 + }, + { + "scope": 1, + "scenario": "baseline", + "metric": "interval_s", + "n": 300, + "mean": 1.0000352026666668, + "median": 1.0002395000000002, + "p95": 1.0089906, + "maximum": 1.0150003, + "minimum": 0.9812941 + }, + { + "scope": 1, + "scenario": "allow", + "metric": "cpu_pct", + "n": 300, + "mean": 0.0026040354623398957, + "median": 0.0, + "p95": 0.0, + "maximum": 0.196429792660654, + "minimum": 0.0 + }, + { + "scope": 1, + "scenario": "allow", + "metric": "private_mib", + "n": 300, + "mean": 1.943046875, + "median": 1.9140625, + "p95": 2.0, + "maximum": 2.0, + "minimum": 1.9140625 + }, + { + "scope": 1, + "scenario": "allow", + "metric": "working_set_mib", + "n": 300, + "mean": 12.527005208333334, + "median": 12.4921875, + "p95": 12.66796875, + "maximum": 12.66796875, + "minimum": 12.4921875 + }, + { + "scope": 1, + "scenario": "allow", + "metric": "threads", + "n": 300, + "mean": 5.98, + "median": 5.0, + "p95": 8.0, + "maximum": 8.0, + "minimum": 5.0 + }, + { + "scope": 1, + "scenario": "allow", + "metric": "handles", + "n": 300, + "mean": 174.66666666666666, + "median": 174.0, + "p95": 176.0, + "maximum": 176.0, + "minimum": 174.0 + }, + { + "scope": 1, + "scenario": "allow", + "metric": "io_read_bytes_s", + "n": 300, + "mean": 0.0, + "median": 0.0, + "p95": 0.0, + "maximum": 0.0, + "minimum": 0.0 + }, + { + "scope": 1, + "scenario": "allow", + "metric": "io_write_bytes_s", + "n": 300, + "mean": 0.0, + "median": 0.0, + "p95": 0.0, + "maximum": 0.0, + "minimum": 0.0 + }, + { + "scope": 1, + "scenario": "allow", + "metric": "system_cpu_pct", + "n": 300, + "mean": 9.331818218824207, + "median": 7.480778769841271, + "p95": 19.7265625, + "maximum": 41.2109375, + "minimum": 3.515625 + }, + { + "scope": 1, + "scenario": "allow", + "metric": "collector_cpu_pct", + "n": 300, + "mean": 0.042966585128608285, + "median": 0.0, + "p95": 0.195573414492276, + "maximum": 0.393158948740528, + "minimum": 0.0 + }, + { + "scope": 1, + "scenario": "allow", + "metric": "interval_s", + "n": 300, + "mean": 1.000050385, + "median": 0.999954199999996, + "p95": 1.01377969999999, + "maximum": 1.0248538, + "minimum": 0.9665964 + }, + { + "scope": 1, + "scenario": "block", + "metric": "cpu_pct", + "n": 300, + "mean": 0.003906120846718285, + "median": 0.0, + "p95": 0.0, + "maximum": 0.195852151016912, + "minimum": 0.0 + }, + { + "scope": 1, + "scenario": "block", + "metric": "private_mib", + "n": 300, + "mean": 3.2445703125, + "median": 3.234375, + "p95": 3.3359375, + "maximum": 3.359375, + "minimum": 3.203125 + }, + { + "scope": 1, + "scenario": "block", + "metric": "working_set_mib", + "n": 300, + "mean": 14.0693359375, + "median": 14.046875, + "p95": 14.265625, + "maximum": 14.26953125, + "minimum": 13.92578125 + }, + { + "scope": 1, + "scenario": "block", + "metric": "threads", + "n": 300, + "mean": 10.366666666666667, + "median": 10.0, + "p95": 13.0, + "maximum": 13.0, + "minimum": 9.0 + }, + { + "scope": 1, + "scenario": "block", + "metric": "handles", + "n": 300, + "mean": 193.48, + "median": 194.0, + "p95": 194.0, + "maximum": 194.0, + "minimum": 192.0 + }, + { + "scope": 1, + "scenario": "block", + "metric": "io_read_bytes_s", + "n": 300, + "mean": 0.0, + "median": 0.0, + "p95": 0.0, + "maximum": 0.0, + "minimum": 0.0 + }, + { + "scope": 1, + "scenario": "block", + "metric": "io_write_bytes_s", + "n": 300, + "mean": 0.0, + "median": 0.0, + "p95": 0.0, + "maximum": 0.0, + "minimum": 0.0 + }, + { + "scope": 1, + "scenario": "block", + "metric": "system_cpu_pct", + "n": 300, + "mean": 8.863378133805679, + "median": 7.324481074481075, + "p95": 17.7734375, + "maximum": 45.7692307692308, + "minimum": 3.90625 + }, + { + "scope": 1, + "scenario": "block", + "metric": "collector_cpu_pct", + "n": 300, + "mean": 0.02929590635038713, + "median": 0.0, + "p95": 0.19531873066751, + "maximum": 0.196246831163168, + "minimum": 0.0 + }, + { + "scope": 1, + "scenario": "block", + "metric": "interval_s", + "n": 300, + "mean": 1.0000330643333333, + "median": 1.000224900000005, + "p95": 1.01188690000004, + "maximum": 1.0183159, + "minimum": 0.979117800000012 + }, + { + "scope": 2, + "scenario": "baseline", + "metric": "system_cpu_pct", + "n": 300, + "mean": 9.093366993306686, + "median": 7.97216021825397, + "p95": 15.0793650793651, + "maximum": 41.6666666666667, + "minimum": 4.296875 + }, + { + "scope": 2, + "scenario": "baseline", + "metric": "collector_cpu_pct", + "n": 300, + "mean": 0.015624672579777866, + "median": 0.0, + "p95": 0.194995787841381, + "maximum": 0.390417844291822, + "minimum": 0.0 + }, + { + "scope": 2, + "scenario": "baseline", + "metric": "interval_s", + "n": 300, + "mean": 1.0000209553333335, + "median": 1.00044265, + "p95": 1.0106092, + "maximum": 1.0194018, + "minimum": 0.981294300000002 + }, + { + "scope": 2, + "scenario": "allow", + "metric": "cpu_pct", + "n": 300, + "mean": 0.0032551589579972104, + "median": 0.0, + "p95": 0.0, + "maximum": 0.19714806679066, + "minimum": 0.0 + }, + { + "scope": 2, + "scenario": "allow", + "metric": "private_mib", + "n": 300, + "mean": 1.9863020833333334, + "median": 1.95703125, + "p95": 2.04296875, + "maximum": 2.04296875, + "minimum": 1.95703125 + }, + { + "scope": 2, + "scenario": "allow", + "metric": "working_set_mib", + "n": 300, + "mean": 12.60734375, + "median": 12.578125, + "p95": 12.75390625, + "maximum": 12.75390625, + "minimum": 12.578125 + }, + { + "scope": 2, + "scenario": "allow", + "metric": "threads", + "n": 300, + "mean": 5.99, + "median": 5.0, + "p95": 8.0, + "maximum": 8.0, + "minimum": 5.0 + }, + { + "scope": 2, + "scenario": "allow", + "metric": "handles", + "n": 300, + "mean": 174.67333333333335, + "median": 174.0, + "p95": 176.0, + "maximum": 176.0, + "minimum": 174.0 + }, + { + "scope": 2, + "scenario": "allow", + "metric": "io_read_bytes_s", + "n": 300, + "mean": 0.0, + "median": 0.0, + "p95": 0.0, + "maximum": 0.0, + "minimum": 0.0 + }, + { + "scope": 2, + "scenario": "allow", + "metric": "io_write_bytes_s", + "n": 300, + "mean": 0.0, + "median": 0.0, + "p95": 0.0, + "maximum": 0.0, + "minimum": 0.0 + }, + { + "scope": 2, + "scenario": "allow", + "metric": "system_cpu_pct", + "n": 300, + "mean": 9.528057373402646, + "median": 8.357699805068226, + "p95": 20.0392927308448, + "maximum": 46.09375, + "minimum": 3.96825396825397 + }, + { + "scope": 2, + "scenario": "allow", + "metric": "collector_cpu_pct", + "n": 300, + "mean": 0.05273357511955474, + "median": 0.0, + "p95": 0.195630438588794, + "maximum": 0.197271146027562, + "minimum": 0.0 + }, + { + "scope": 2, + "scenario": "allow", + "metric": "interval_s", + "n": 300, + "mean": 1.0000151683333331, + "median": 0.999932800000009, + "p95": 1.01079730000001, + "maximum": 1.01451029999998, + "minimum": 0.985216799999996 + }, + { + "scope": 2, + "scenario": "block", + "metric": "cpu_pct", + "n": 300, + "mean": 0.008463486718889725, + "median": 0.0, + "p95": 0.0, + "maximum": 0.197048179181501, + "minimum": 0.0 + }, + { + "scope": 2, + "scenario": "block", + "metric": "private_mib", + "n": 300, + "mean": 3.2369270833333332, + "median": 3.244140625, + "p95": 3.2890625, + "maximum": 3.2890625, + "minimum": 3.19140625 + }, + { + "scope": 2, + "scenario": "block", + "metric": "working_set_mib", + "n": 300, + "mean": 13.669856770833333, + "median": 14.01953125, + "p95": 14.0390625, + "maximum": 14.04296875, + "minimum": 13.0234375 + }, + { + "scope": 2, + "scenario": "block", + "metric": "threads", + "n": 300, + "mean": 10.26, + "median": 10.0, + "p95": 12.0, + "maximum": 12.0, + "minimum": 9.0 + }, + { + "scope": 2, + "scenario": "block", + "metric": "handles", + "n": 300, + "mean": 193.49333333333334, + "median": 194.0, + "p95": 194.0, + "maximum": 194.0, + "minimum": 192.0 + }, + { + "scope": 2, + "scenario": "block", + "metric": "io_read_bytes_s", + "n": 300, + "mean": 0.0, + "median": 0.0, + "p95": 0.0, + "maximum": 0.0, + "minimum": 0.0 + }, + { + "scope": 2, + "scenario": "block", + "metric": "io_write_bytes_s", + "n": 300, + "mean": 0.0, + "median": 0.0, + "p95": 0.0, + "maximum": 0.0, + "minimum": 0.0 + }, + { + "scope": 2, + "scenario": "block", + "metric": "system_cpu_pct", + "n": 300, + "mean": 9.668837806705387, + "median": 8.0078125, + "p95": 19.53125, + "maximum": 41.468253968254, + "minimum": 3.515625 + }, + { + "scope": 2, + "scenario": "block", + "metric": "collector_cpu_pct", + "n": 300, + "mean": 0.0546871449528259, + "median": 0.0, + "p95": 0.195682437648375, + "maximum": 0.389974016374465, + "minimum": 0.0 + }, + { + "scope": 2, + "scenario": "block", + "metric": "interval_s", + "n": 300, + "mean": 1.0000064923333332, + "median": 1.0000182, + "p95": 1.01092840000001, + "maximum": 1.015154, + "minimum": 0.98461309999999 + }, + { + "scope": 3, + "scenario": "baseline", + "metric": "system_cpu_pct", + "n": 300, + "mean": 9.41169548279246, + "median": 7.4609375, + "p95": 20.1171875, + "maximum": 86.9230769230769, + "minimum": 3.515625 + }, + { + "scope": 3, + "scenario": "baseline", + "metric": "collector_cpu_pct", + "n": 300, + "mean": 0.0052082565098137265, + "median": 0.0, + "p95": 0.0, + "maximum": 0.196882381041712, + "minimum": 0.0 + }, + { + "scope": 3, + "scenario": "baseline", + "metric": "interval_s", + "n": 300, + "mean": 1.0000147503333332, + "median": 1.0003615499999952, + "p95": 1.0121634, + "maximum": 1.01521399999996, + "minimum": 0.980372000000003 + }, + { + "scope": 3, + "scenario": "allow", + "metric": "cpu_pct", + "n": 300, + "mean": 0.0013020677796996792, + "median": 0.0, + "p95": 0.0, + "maximum": 0.194801185847389, + "minimum": 0.0 + }, + { + "scope": 3, + "scenario": "allow", + "metric": "private_mib", + "n": 300, + "mean": 1.9357682291666667, + "median": 1.90625, + "p95": 1.99609375, + "maximum": 1.99609375, + "minimum": 1.90625 + }, + { + "scope": 3, + "scenario": "allow", + "metric": "working_set_mib", + "n": 300, + "mean": 12.568216145833333, + "median": 12.5390625, + "p95": 12.71484375, + "maximum": 12.71484375, + "minimum": 12.5390625 + }, + { + "scope": 3, + "scenario": "allow", + "metric": "threads", + "n": 300, + "mean": 5.986666666666666, + "median": 5.0, + "p95": 8.0, + "maximum": 8.0, + "minimum": 5.0 + }, + { + "scope": 3, + "scenario": "allow", + "metric": "handles", + "n": 300, + "mean": 174.67333333333335, + "median": 174.0, + "p95": 176.0, + "maximum": 176.0, + "minimum": 174.0 + }, + { + "scope": 3, + "scenario": "allow", + "metric": "io_read_bytes_s", + "n": 300, + "mean": 0.0, + "median": 0.0, + "p95": 0.0, + "maximum": 0.0, + "minimum": 0.0 + }, + { + "scope": 3, + "scenario": "allow", + "metric": "io_write_bytes_s", + "n": 300, + "mean": 0.0, + "median": 0.0, + "p95": 0.0, + "maximum": 0.0, + "minimum": 0.0 + }, + { + "scope": 3, + "scenario": "allow", + "metric": "system_cpu_pct", + "n": 300, + "mean": 8.56093271608931, + "median": 7.421875, + "p95": 15.625, + "maximum": 37.109375, + "minimum": 3.90625 + }, + { + "scope": 3, + "scenario": "allow", + "metric": "collector_cpu_pct", + "n": 300, + "mean": 0.052733745077837124, + "median": 0.0, + "p95": 0.197443288222162, + "maximum": 0.39194420580791, + "minimum": 0.0 + }, + { + "scope": 3, + "scenario": "allow", + "metric": "interval_s", + "n": 300, + "mean": 1.0000119453333334, + "median": 1.000105950000005, + "p95": 1.01430719999999, + "maximum": 1.0191368, + "minimum": 0.978705200000036 + }, + { + "scope": 3, + "scenario": "block", + "metric": "cpu_pct", + "n": 300, + "mean": 0.002604138477735689, + "median": 0.0, + "p95": 0.0, + "maximum": 0.196122840351767, + "minimum": 0.0 + }, + { + "scope": 3, + "scenario": "block", + "metric": "private_mib", + "n": 300, + "mean": 3.2722135416666664, + "median": 3.26171875, + "p95": 3.3515625, + "maximum": 3.3515625, + "minimum": 3.23046875 + }, + { + "scope": 3, + "scenario": "block", + "metric": "working_set_mib", + "n": 300, + "mean": 14.100286458333333, + "median": 14.078125, + "p95": 14.27734375, + "maximum": 14.27734375, + "minimum": 14.0625 + }, + { + "scope": 3, + "scenario": "block", + "metric": "threads", + "n": 300, + "mean": 10.373333333333333, + "median": 10.0, + "p95": 13.0, + "maximum": 13.0, + "minimum": 9.0 + }, + { + "scope": 3, + "scenario": "block", + "metric": "handles", + "n": 300, + "mean": 193.49333333333334, + "median": 194.0, + "p95": 194.0, + "maximum": 194.0, + "minimum": 192.0 + }, + { + "scope": 3, + "scenario": "block", + "metric": "io_read_bytes_s", + "n": 300, + "mean": 0.0, + "median": 0.0, + "p95": 0.0, + "maximum": 0.0, + "minimum": 0.0 + }, + { + "scope": 3, + "scenario": "block", + "metric": "io_write_bytes_s", + "n": 300, + "mean": 0.0, + "median": 0.0, + "p95": 0.0, + "maximum": 0.0, + "minimum": 0.0 + }, + { + "scope": 3, + "scenario": "block", + "metric": "system_cpu_pct", + "n": 300, + "mean": 8.548628772863976, + "median": 7.2601142634357, + "p95": 15.1923076923077, + "maximum": 39.2857142857143, + "minimum": 2.57936507936508 + }, + { + "scope": 3, + "scenario": "block", + "metric": "collector_cpu_pct", + "n": 300, + "mean": 0.03645793868829966, + "median": 0.0, + "p95": 0.195363626661097, + "maximum": 0.197120668177123, + "minimum": 0.0 + }, + { + "scope": 3, + "scenario": "block", + "metric": "interval_s", + "n": 300, + "mean": 1.0000108246666666, + "median": 1.0001475499999999, + "p95": 1.0142349, + "maximum": 1.01716189999999, + "minimum": 0.981509700000004 + } +] \ No newline at end of file diff --git a/docs/benchmarks/2026-09-29-v0.2.0/validation.json b/docs/benchmarks/2026-09-29-v0.2.0/validation.json new file mode 100644 index 0000000..959aaae --- /dev/null +++ b/docs/benchmarks/2026-09-29-v0.2.0/validation.json @@ -0,0 +1,78 @@ +{ + "passed": true, + "samples": 2700, + "runs": [ + { + "run": 1, + "scenario": "baseline", + "samples": 300, + "elapsed_s": 300.0117647, + "intervals_over_1_25s": 0, + "max_interval_s": 1.0150003 + }, + { + "run": 1, + "scenario": "allow", + "samples": 300, + "elapsed_s": 300.0151189, + "intervals_over_1_25s": 0, + "max_interval_s": 1.0248538 + }, + { + "run": 1, + "scenario": "block", + "samples": 300, + "elapsed_s": 300.0099228, + "intervals_over_1_25s": 0, + "max_interval_s": 1.0183159 + }, + { + "run": 2, + "scenario": "baseline", + "samples": 300, + "elapsed_s": 300.0062893, + "intervals_over_1_25s": 0, + "max_interval_s": 1.0194018 + }, + { + "run": 2, + "scenario": "allow", + "samples": 300, + "elapsed_s": 300.0045551, + "intervals_over_1_25s": 0, + "max_interval_s": 1.01451029999998 + }, + { + "run": 2, + "scenario": "block", + "samples": 300, + "elapsed_s": 300.001951, + "intervals_over_1_25s": 0, + "max_interval_s": 1.015154 + }, + { + "run": 3, + "scenario": "baseline", + "samples": 300, + "elapsed_s": 300.0044275, + "intervals_over_1_25s": 0, + "max_interval_s": 1.01521399999996 + }, + { + "run": 3, + "scenario": "allow", + "samples": 300, + "elapsed_s": 300.0035865, + "intervals_over_1_25s": 0, + "max_interval_s": 1.0191368 + }, + { + "run": 3, + "scenario": "block", + "samples": 300, + "elapsed_s": 300.0032511, + "intervals_over_1_25s": 0, + "max_interval_s": 1.01716189999999 + } + ] +} \ No newline at end of file diff --git a/docs/benchmarks/2026-09-29-v0.2.0/verify.py b/docs/benchmarks/2026-09-29-v0.2.0/verify.py new file mode 100644 index 0000000..9977ace --- /dev/null +++ b/docs/benchmarks/2026-09-29-v0.2.0/verify.py @@ -0,0 +1,33 @@ +"""Read-only integrity and reproducibility check for this archived campaign.""" +import hashlib +from pathlib import Path +import shutil +import subprocess +import sys +import tempfile + +root = Path(__file__).resolve().parent +manifest = root / 'SHA256SUMS.txt' +expected = {} +for line in manifest.read_text(encoding='utf-8-sig').splitlines(): + digest, name = line.split(' ', 1) + path = (root / name).resolve() + if not path.is_relative_to(root) or name in expected: + raise SystemExit('Invalid or duplicate manifest path') + expected[name] = digest +actual = {p.relative_to(root).as_posix() for p in root.rglob('*') if p.is_file() and p != manifest} +if actual != set(expected): + raise SystemExit('Manifest coverage differs from directory contents') +for name, digest in expected.items(): + if hashlib.sha256((root / name).read_bytes()).hexdigest() != digest: + raise SystemExit(f'Checksum mismatch: {name}') +with tempfile.TemporaryDirectory(prefix='wardoff-evidence-') as temp: + copy = Path(temp) / 'campaign' + shutil.copytree(root, copy) + result = subprocess.run([sys.executable, str(copy/'analyze.py')], capture_output=True, text=True) + if result.returncode: + raise SystemExit('Analysis failed: ' + result.stderr) + for name in ['summary.csv', 'summary.json', 'validation.json', 'RAPPORT.md']: + if (root/name).read_text(encoding='utf-8') != (copy/name).read_text(encoding='utf-8'): + raise SystemExit(f'Recomputed artifact differs: {name}') +print(f'PASS: {len(expected)} file hashes verified; statistics and report reproduced; stored evidence unchanged.') From e911c40333c4358cd42bf630916d7b9537781981 Mon Sep 17 00:00:00 2001 From: RomainROCH Date: Fri, 2 Oct 2026 07:24:59 +0200 Subject: [PATCH 02/15] fix: use owned requests for idle-sleep protection --- .github/copilot-instructions.md | 10 +- CHANGELOG.md | 5 +- CLAUDE.md | 2 +- CONTRIBUTING.md | 2 +- Cargo.lock | 2 +- Cargo.toml | 1 + PLAN.md | 2 +- README.md | 7 +- docs/ARCHITECTURE.md | 46 ++- docs/COMPARISON.md | 6 +- docs/MVP_VALIDATION_MATRIX.md | 100 ++++++- docs/OUTREACH_DRAFTS.md | 16 +- docs/RELEASE_NOTES_DRAFT.md | 2 +- docs/WINDOWS_SHUTDOWN_LAYERS.md | 26 +- src/blocker/mod.rs | 11 +- src/blocker/shutdown.rs | 18 +- src/blocker/sleep.rs | 498 ++++++++++++++++++++++---------- src/cli.rs | 4 +- src/logger/mod.rs | 2 +- src/main.rs | 6 + tests/smoke_test.ps1 | 57 +++- 21 files changed, 597 insertions(+), 226 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index d6a4d69..3129fea 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -50,7 +50,7 @@ If a user asks only **"what's next?"**, answer from repo docs instead of inventi ## High-level architecture -- `Wardoff` is a Windows-only Rust utility for preventing or aborting unwanted shutdown, reboot, sleep, hibernate, and related power transitions. The intended target is `x86_64-pc-windows-msvc`. +- `Wardoff` is a Windows-only Rust utility for protecting supported shutdown/reboot paths and preventing idle sleep and automatic display timeout. The intended target is `x86_64-pc-windows-msvc`. - The product has two control surfaces that should stay aligned: - a tray/background app with Block/Allow state and optional hidden mode - a CLI exposing `--block`, `--allow`, `--status`, `--hide`, `--log`, `--tail`, `--autostart on|off`, and `--version`, with `--status` expected to produce JSON for scripting @@ -66,7 +66,7 @@ If a user asks only **"what's next?"**, answer from repo docs instead of inventi 4. remote shutdown protection via repeated `AbortSystemShutdown(NULL)` polling - Be explicit that Layer 3 can skip cleanly on editions such as LTSC where `\Microsoft\Windows\UpdateOrchestrator\Reboot` is absent; that is normal behavior, not a failure. - For detailed layer-by-layer behavior and the cautious wording around ETW/local shutdown handling, read `docs/WINDOWS_SHUTDOWN_LAYERS.md`. -- Sleep/hibernate/display blocking is a separate toggle and is expected to use `SetThreadExecutionState(...)`. +- Idle-sleep/display-timeout prevention follows Block/Allow through process-owned Power Requests. Follow `docs/ARCHITECTURE.md` and `docs/MVP_VALIDATION_MATRIX.md` for lifecycle, resume behavior and Windows limits; do not promise an explicit Sleep/Hibernate veto. - Autostart is handled via Task Scheduler rather than the `Run` registry key so elevated scenarios can be handled correctly. ## Implemented module structure @@ -78,7 +78,7 @@ If a user asks only **"what's next?"**, answer from repo docs instead of inventi - `src/blocker/local.rs`: current local shutdown worker implementation; keep its user-facing documentation conservative and aligned with MVP scope decisions - `src/blocker/update.rs`: UpdateOrchestrator task monitoring and restore logic - `src/blocker/remote.rs`: Layer 4 `AbortSystemShutdownW(None)` polling loop -- `src/blocker/sleep.rs`: `SetThreadExecutionState(...)` blocker +- `src/blocker/sleep.rs`: owned system/display Power Requests - `src/blocker/abort.rs`: shared shutdown-abort privilege and result helpers - `src/autostart.rs`: scheduled-task autostart management - `src/instance.rs` and `src/ipc.rs`: single-instance ownership plus the named-pipe control and read-only status paths @@ -91,7 +91,7 @@ If a user asks only **"what's next?"**, answer from repo docs instead of inventi - Re-read `PLAN.md` before making major structural decisions. Use it as the roadmap and scope boundary document, while `README.md` stays the quickest current-state summary. - Preserve the distinction between MVP and later phases: - - MVP is the safe, official-API release: standard shutdown blocking, UpdateOrchestrator handling, remote abort loop, tray basics, CLI basics, Task Scheduler autostart, sleep/hibernate/display blocking, and simple file logging + - MVP is the safe, official-API release: standard shutdown blocking, UpdateOrchestrator handling, remote abort loop, tray basics, CLI basics, Task Scheduler autostart, idle-sleep/display-timeout prevention, and simple file logging - aggressive IFEO mode, Windows Event Log, profiles, timer, and toast notifications belong to later phases unless the plan is updated - Be explicit about elevation boundaries. Update protection and aggressive `shutdown.exe` interception are planned as admin-only features; tray, UI, and CLI flows should surface that requirement clearly rather than failing silently. - Prefer official Windows APIs first. Aggressive behavior is opt-in and should carry strong warnings because it may trigger EDR tooling. @@ -113,7 +113,7 @@ Only these features belong in the current phase: - Layer 1: ShutdownBlockReasonCreate + WM_QUERYENDSESSION + SetProcessShutdownParameters - Layer 3: Task Scheduler UpdateOrchestrator\Reboot disable/re-check - Layer 4: AbortSystemShutdown polling loop -- Sleep/hibernate/screensaver blocking via SetThreadExecutionState +- Idle-sleep/display-timeout prevention via Power Requests; no screensaver, lock or explicit Sleep/Hibernate veto - Tray icon: Block/Allow toggle, right-click menu (Block, Allow, Shutdown, Reboot, Sleep, Hibernate, Quit) - CLI: --block, --allow, --status (JSON output), --hide, --log, --tail, --autostart on|off, --version - Auto-start via Task Scheduler diff --git a/CHANGELOG.md b/CHANGELOG.md index 0358b0c..3fce0e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ All notable changes to this project will be documented in this file. ## [Unreleased] ### Changed +- Replaced the sleep layer's periodic `SetThreadExecutionState` worker with owned system/display Power Requests and an identifiable Wardoff reason. Activation failures now reach the coordinator synchronously, and Allow/exit release the request object. +- Kept the existing tray wake-restore flow and added only a request-pair renewal to the existing resume callback when the runtime remains in Block, based on the documented termination of requests at user-initiated sleep. No additional resume state machine, display notifications or polling was introduced. +- Corrected sleep claims throughout maintained docs, CLI help and runtime logs: protection concerns idle sleep and automatic display timeout, subject to Windows policy. Explicit Sleep/Hibernate are not vetoed; Modern Standby on battery has additional limits. The original v0.1.0 hibernation claim below was too broad. - Hardened the session-scoped `WardoffControl` and `WardoffStatus` pipes against same-session squatting by claiming first pipe instances with bounded startup retries, failing closed until both IPC servers own their initial instances, and probing the singleton before `wardoff --status` opens IPC endpoints. - Scoped the singleton mutex plus the `WardoffControl` and `WardoffStatus` named pipes to the current Windows session so cross-session mutex or pipe squatting no longer breaks Wardoff's single-instance model across all sessions. - Hardened structured JSONL logging under `%LOCALAPPDATA%\Wardoff\logs` so Wardoff now creates its managed log directories one component at a time, refuses reparse points in the managed log tree and rotated files, and uses reparse-aware no-follow opens for active and read-only log file access. @@ -34,7 +37,7 @@ All notable changes to this project will be documented in this file. - Layer 1 interactive shutdown and sign-out blocking - Layer 3 protection for `\Microsoft\Windows\UpdateOrchestrator\Reboot` - Layer 4 best-effort remote shutdown abort polling -- Sleep, hibernate, and display-idle blocking via `SetThreadExecutionState(...)` +- Idle-sleep and display-timeout prevention via `SetThreadExecutionState(...)` (wording corrected: the original release description overstated hibernation protection) - Tray UI with Block, Allow, Shutdown, Reboot, Sleep, Hibernate, and Quit actions - CLI support for `--block`, `--allow`, `--hide`, `--status`, `--log`, `--tail`, and `--autostart` - Structured rotating JSONL logging diff --git a/CLAUDE.md b/CLAUDE.md index 4f3a0a8..403e931 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -25,7 +25,7 @@ Business model : @docs/BUSINESS_MODEL.md `cargo test` couvre les tests unitaires et tourne sans privilèges. Les couches 3 (Update Orchestrator) et 4 (remote shutdown) ne s'arment qu'en session élevée — leur validation est manuelle. -Procédure complète et matrice : @docs/MANUAL_VALIDATION_PLAN.md +Procédure complète et matrice : @docs/MVP_VALIDATION_MATRIX.md # Git diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 411539a..bc7f88b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -63,7 +63,7 @@ Keep documentation and PR descriptions aligned with the current supported MVP su - interactive shutdown/sign-out blocking is implemented - Update Orchestrator reboot-task protection is implemented - remote shutdown abort polling is implemented -- sleep/hibernate/display-idle blocking is implemented +- idle-sleep and automatic display-timeout prevention are implemented; explicit Sleep/Hibernate remain controlled by Windows - tray, CLI, logging, IPC, autostart, and single-instance coordination are implemented Do **not** claim the following as implemented unless your change really adds and validates them: diff --git a/Cargo.lock b/Cargo.lock index e6b4967..17d0228 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1446,7 +1446,7 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "wardoff" -version = "0.1.0" +version = "0.2.0" dependencies = [ "chrono", "clap", diff --git a/Cargo.toml b/Cargo.toml index c1cd0c2..38f6d15 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ windows = { version = "0.62.2", features = [ "Win32_System_Diagnostics_Etw", "Win32_System_Shutdown", "Win32_System_Power", + "Win32_System_SystemServices", "Win32_System_IO", "Win32_System_Pipes", "Win32_System_Time", diff --git a/PLAN.md b/PLAN.md index ee5c62c..4372e0c 100644 --- a/PLAN.md +++ b/PLAN.md @@ -22,7 +22,7 @@ The branch already contains a working runtime plus CLI centered on one main prom - Layer 1 interactive shutdown and sign-out blocking - Layer 3 protection for `\Microsoft\Windows\UpdateOrchestrator\Reboot` when that task exists on the machine - Layer 4 best-effort remote shutdown abort polling -- sleep, hibernate, and display-idle blocking +- idle-sleep and automatic display-timeout prevention through Power Requests (no explicit Sleep/Hibernate veto) - tray UI with Block/Allow state and power actions - CLI control surface - structured rotating JSONL logs diff --git a/README.md b/README.md index 70fde46..885753d 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ ![Rust](https://img.shields.io/badge/language-Rust-orange) ![Windows](https://img.shields.io/badge/platform-Windows%2010%2B-blue) -*Open-source Windows shutdown/reboot/sleep blocker* +*Open-source Windows shutdown/reboot protection and idle-sleep prevention* Wardoff is a Windows-native Rust utility for people who want a visible, scriptable way to keep a machine in a protected **Block** state during gaming sessions, overnight jobs, remote work, or maintenance windows. The project is intentionally honest about what the current MVP does today and what still belongs to later releases. @@ -39,7 +39,7 @@ Wardoff currently ships a working tray/runtime app plus CLI with these MVP-level - Layer 1 interactive shutdown and sign-out blocking through `WM_QUERYENDSESSION`, `ShutdownBlockReasonCreate()`, and `SetProcessShutdownParameters()` - Layer 3 protection for the scheduled task `Microsoft\Windows\UpdateOrchestrator\Reboot` when that task exists - Layer 4 best-effort remote shutdown abort polling with `AbortSystemShutdownW(None)` -- sleep, hibernate, and display-idle blocking via `SetThreadExecutionState(...)` +- idle-sleep and automatic display-timeout prevention through Windows Power Requests, subject to Windows power policy - structured rotating JSONL logging under `%LOCALAPPDATA%\Wardoff\logs\` - single-instance coordination so later CLI calls can control the primary runtime - Task Scheduler autostart management @@ -75,6 +75,9 @@ For why IFEO stays out of the current MVP, see [`docs/IFEO_WARNING.md`](docs/IFE ## Honest limits +- **Sleep protection means idle-sleep prevention.** Explicit Sleep or Hibernate, power-button actions and lid closure can still suspend the PC. Wardoff does not block the screen saver or promise to prevent session locking. +- Windows power policy and request overrides can ignore keep-awake requests. On Modern Standby running on battery, system-required requests expire five minutes after the system sleep timeout expires. See the [power behavior matrix and Microsoft sources](docs/MVP_VALIDATION_MATRIX.md#idle-power-behavior-and-windows-limits). +- `layers.sleep=true` means Wardoff acquired its system/display request pair; it is not a guarantee that Windows will refuse every power transition. - **Wardoff does not promise to stop `shutdown /t 0 /f`.** A forced zero-second local shutdown is outside what a normal user-space app can reliably block. - Windows Update reboot-task protection requires administrator rights. - Remote shutdown abort logic only helps when Windows still exposes a timeout window and the process has the rights required for `AbortSystemShutdownW(None)`. diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 2656fc9..282fe05 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -51,7 +51,7 @@ Contains the layered protection logic. - `local.rs` — local `shutdown.exe` ETW detection and best-effort abort attempts - `update.rs` — Layer 3 Update Orchestrator reboot-task protection - `remote.rs` — Layer 4 remote shutdown abort polling -- `sleep.rs` — sleep/hibernate/display-idle blocking +- `sleep.rs` — idle-sleep and automatic display-timeout prevention - `abort.rs` — shared `AbortSystemShutdownW(None)` helpers used by local and remote shutdown code ### `src/tray/` @@ -98,7 +98,7 @@ It holds: - `LocalShutdownBlocker` for local `shutdown.exe` handling - `UpdateRebootBlocker` for Update Orchestrator task protection - `RemoteShutdownBlocker` for remote shutdown polling -- `SleepBlocker` for execution-state-based power-state blocking +- `SleepBlocker` for process-owned idle-power requests It also owns the current `BlockerMode`: @@ -142,6 +142,46 @@ When the runtime returns to Allow mode, it: If a Block-mode transition fails partway through, `BlockerCoordinator` rolls back the already-started layers so the process does not remain half-armed. +## Idle-power request ownership (intentional architecture change) + +The sleep layer uses one process-owned Power Request object with +`PowerRequestSystemRequired` and `PowerRequestDisplayRequired`, replacing the +thread-affine `SetThreadExecutionState` worker and its 30-second refresh. +The purpose is explicit ownership, synchronous activation errors, and a Wardoff +reason in `powercfg /requests`; this does not add a veto over explicit sleep or +hibernate. The existing `windows` dependency provides these APIs. + +Block acquires both requests transactionally. Allow, rollback, normal exit and +forced-session cleanup release them and close the handle. The handle is private +to this process and is never duplicated or inherited. Windows closes process +handles on termination; forced termination must also be checked in the smoke +test. No power plan, registry, policy, button or lid setting is written. + +The existing resume callback is sufficient. Microsoft's +[PowerSetRequest contract](https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-powersetrequest) +documents termination of requests at user-initiated sleep entry. When the +runtime remains in Block, that callback replaces the old owned request object +and reacquires the pair. It does not assume that the handle itself becomes +invalid. A tray power action already releases requests through Allow and +reacquires them through the existing restore-to-Block path. There is no new +resume state machine, display/presence subscription, timer, or battery-limit +bypass. Duplicate resume notifications must never accumulate request counts. +Physical S3/S4 and Modern Standby resume behavior remains a manual validation +requirement; API lifecycle tests are not evidence of an actual sleep transition. + +`layers.sleep` retains its JSON name and means that Wardoff acquired the request +pair, not that Windows guarantees every power transition is prevented. System +policy can ignore requests. A failed initial acquisition uses the coordinator's +existing rollback; a failed resume acquisition reports an inactive sleep layer +and an error without disabling unrelated shutdown protections. + +Away Mode is intentionally excluded: it substitutes apparent sleep on eligible +S3 systems, depends on the existing Allow Away Mode policy, and has no Modern +Standby coverage. Execution-required requests do not add the desired idle-sleep +guarantee. Neither the legacy API as a fallback nor obsolete suspend veto events +are combined with this backend. See the [validation matrix](MVP_VALIDATION_MATRIX.md) +for the power contract, source references, and hardware acceptance procedure. + ## Process model and single-instance behavior Wardoff is designed to have one primary runtime per Windows session boundary enforced by a named mutex. @@ -376,7 +416,7 @@ For current documentation and review, treat these as supported, visible runtime - Layer 1 interactive blocking - Layer 3 update reboot-task protection - Layer 4 remote abort polling -- sleep/hibernate/display-idle blocking +- idle-sleep and automatic display-timeout prevention - tray UI - CLI plus IPC control - structured JSONL logging diff --git a/docs/COMPARISON.md b/docs/COMPARISON.md index 6c2b1f4..1fdb34c 100644 --- a/docs/COMPARISON.md +++ b/docs/COMPARISON.md @@ -6,7 +6,7 @@ | Product | Source model | Current focus | What Wardoff can honestly claim today | Important gaps or planned items | | --- | --- | --- | --- | --- | -| Wardoff | MIT, open source | Transparent shutdown and power-state control for modern Windows | Interactive shutdown/sign-out blocking, Update Orchestrator reboot-task protection, remote abort polling, sleep/hibernate/display-idle blocking, tray UI, CLI, JSONL logs, single-instance IPC, autostart | No IFEO mode, no Windows Event Log integration, and no promise to stop local `shutdown /t 0 /f` | +| Wardoff | MIT, open source | Transparent shutdown and power-state control for modern Windows | Interactive shutdown/sign-out blocking, Update Orchestrator reboot-task protection, remote abort polling, idle-sleep and automatic display-timeout prevention, tray UI, CLI, JSONL logs, single-instance IPC, autostart | No IFEO mode, no Windows Event Log integration, and no promise to stop local `shutdown /t 0 /f` | | ShutdownBlocker | Closed freeware | Traditional shutdown blocking utility | Comparable motivation around shutdown prevention | Closed implementation, older maintenance history, and behavior cannot be audited here | | ShutdownGuard | MIT, open source | Historical shutdown-blocking approach | Open-source reference point | Archived and unsupported | | Don't Sleep | Closed freeware | Keep-awake / power-state prevention | Strong overlap on sleep-prevention use cases | Not positioned as a transparent layered shutdown-control tool | @@ -21,7 +21,7 @@ Wardoff's current MVP is best described as a conservative, transparent Windows r - Layer 1 interactive shutdown and sign-out blocking - Layer 3 protection for `\Microsoft\Windows\UpdateOrchestrator\Reboot` - Layer 4 best-effort remote shutdown abort polling -- sleep, hibernate, and display-idle blocking via `SetThreadExecutionState(...)` +- idle-sleep and automatic display-timeout prevention through Power Requests, subject to Windows power policy - tray controls for Block, Allow, Shutdown, Reboot, Sleep, Hibernate, and Quit - CLI control surface for `--block`, `--allow`, `--hide`, `--status`, `--log`, `--tail`, and `--autostart` - structured rotating JSONL logs @@ -115,7 +115,7 @@ Wardoff's current 0.1.0 positioning is: - open and auditable - conservative in what it claims -- useful today for interactive shutdown blocking, update-task protection, remote abort polling, and sleep prevention +- useful today for interactive shutdown blocking, update-task protection, remote abort polling, and idle-sleep prevention - intentionally cautious about local `shutdown.exe` That last point is the most important comparison decision: diff --git a/docs/MVP_VALIDATION_MATRIX.md b/docs/MVP_VALIDATION_MATRIX.md index 093815c..c0555df 100644 --- a/docs/MVP_VALIDATION_MATRIX.md +++ b/docs/MVP_VALIDATION_MATRIX.md @@ -9,7 +9,7 @@ This matrix covers only the current MVP surface: - Layer 1 interactive shutdown and sign-out blocking - Layer 3 Update Orchestrator reboot-task protection - Layer 4 best-effort remote shutdown abort polling -- sleep, hibernate, and display-idle blocking +- idle-sleep and automatic display-timeout prevention, subject to Windows policy - tray behavior - CLI behavior - IPC and single-instance behavior @@ -31,7 +31,7 @@ Keep validation language conservative: | Logs | `%LOCALAPPDATA%\Wardoff\logs\wardoff.jsonl` | | Admin rights | Required for Layer 3, Layer 4, Update Orchestrator task checks, and autostart task changes | | Trusted install path | Required if you expect `wardoff --autostart on` to create or update the `Wardoff` task; from an untrusted or user-writable path Wardoff should refuse safely and leave the scheduled-task state unchanged | -| Disposable VM | Required for shutdown, reboot, sign-out, sleep, hibernate, or remote-shutdown checks that could disrupt the host | +| Disruptive tests | Use a disposable VM for shutdown/reboot/sign-out checks; use prepared test hardware or a capable VM for sleep/hibernate checks | ## Automated checks already covered by `tests\smoke_test.ps1` @@ -40,21 +40,27 @@ Keep validation language conservative: | Release build | `cargo build --release` succeeds | No | | Release artifact | `target\release\wardoff.exe` exists | No | | CLI help | `wardoff --help` exits `0` and mentions `wardoff` | No | -| CLI version | `wardoff --version` exits `0` and prints `wardoff 0.1.0` | No | +| CLI version | `wardoff --version` exits `0` and prints the package version | No | | Inactive status | `wardoff --status` exits `1` and returns `{"state":"inactive"}` when no instance is running | No | | Hidden block startup | `Start-Process ... --block --hide` keeps the runtime alive | No | | Active status JSON | `wardoff --status` exits `0`, returns `state=block`, and includes `layers.local_shutdown` as a boolean | No | -| Sleep/display request visibility | `powercfg /requests` mentions Wardoff while blocking is active, when the session allows that query | No | +| Sleep/display request visibility | `powercfg /requests` contains the Wardoff reason for both requests in Block; it does not prove a physical transition was prevented | Query may require elevation | +| Block/Allow/Block lifecycle | `layers.sleep` is true/false/true; requests disappear in Allow while the same runtime remains alive, then acquisition is reported again in Block | Query may require elevation | +| Abrupt termination | The owned test runtime is killed directly from Block, without Allow; its requests disappear | Query may require elevation | | Structured logging | `%LOCALAPPDATA%\Wardoff\logs\wardoff.jsonl` exists, appends new lines, and contains valid JSON | No | | Layer 3 status | Elevated status reports `layers.update=true` when `\Microsoft\Windows\UpdateOrchestrator\Reboot` exists; if the task is absent, note the normal skip/defer case instead of treating it as a failure | Yes | | Layer 4 status | Elevated status reports `layers.remote=true` | Yes | | Update task access | `schtasks /query /tn Microsoft\Windows\UpdateOrchestrator\Reboot` either succeeds or shows the task is absent on this machine; absence is a normal Layer 3 skip/defer case | Yes | | Autostart task lifecycle | From a trusted admin-writable location, `wardoff --autostart on` creates the `Wardoff` task and `--autostart off` removes it; from an untrusted or user-writable path Wardoff refuses safely and leaves the task state unchanged | Yes | -| Cleanup path | `wardoff --allow` plus process cleanup stops the background instance | No | +| Cleanup path | Direct termination from Block stops the owned background instance; a final cleanup helper handles test failures | No | | Sleep/display cleanup | `powercfg /requests` no longer mentions Wardoff after cleanup, when the session allows that query | No | The smoke test checks status visibility for local shutdown handling, but that is **not** the same as promising reliable interception of all local shutdown paths. +For an explicit cross target, set `CARGO_BUILD_TARGET=x86_64-pc-windows-msvc` +in the test process environment. The smoke script uses that target for its build +and resolves `target/x86_64-pc-windows-msvc/release/wardoff.exe` accordingly. + ## Manual non-disruptive checks Use a normal desktop session for these checks. They should not require actually shutting down, rebooting, sleeping, or hibernating the machine. @@ -95,14 +101,92 @@ Do these only in a disposable VM or similarly safe environment. | Area | Check | Expected result | | --- | --- | --- | | Layer 1 interactive shutdown | With Wardoff in Block mode, attempt a normal interactive shutdown or sign-out from Windows | Windows gives Wardoff the chance to block the interactive path | -| Windows Sleep blocking | With Wardoff in Block mode, trigger Sleep from the Windows power menu | The VM stays awake while Block mode is active | -| Windows Hibernate blocking | With Wardoff in Block mode, trigger Hibernate from the Windows power menu | The VM does not hibernate while Block mode is active | +| Windows explicit Sleep limit | With Wardoff in Block mode, trigger Sleep from the Windows power menu on a sleep-capable test machine | Windows may sleep; Wardoff does not veto the request. After resume, check that Block's idle-power requests are acquired again | +| Windows explicit Hibernate limit | With Wardoff in Block mode, trigger Hibernate on a test machine where it is already available | Windows may hibernate; Wardoff does not veto the request. After resume, check the mode, requests and logs | | Tray Sleep wake-restore | With Wardoff in Block mode, trigger Sleep from the tray menu, then wake the VM | Wardoff temporarily switches to Allow so Sleep can proceed, then returns to Block after wake and records the wake restore behavior with a `block_restored_after_wake` log event | | Tray Hibernate wake-restore | With Wardoff in Block mode, trigger Hibernate from the tray menu, then resume the VM | Wardoff temporarily switches to Allow so Hibernate can proceed, then returns to Block after resume and records the wake restore behavior with a `block_restored_after_wake` log event | -| Allow-mode release | Switch to Allow mode and retry Sleep or Hibernate | Windows proceeds normally once Wardoff is no longer blocking | +| Allow-mode release | Switch to Allow while the runtime remains alive, then wait for idle sleep/display timeout | Wardoff's requests are absent; Windows follows its existing settings and any requests from other applications | | Remote shutdown abort | From a second machine or remote management session, issue a timed remote shutdown while Wardoff is running elevated in Block mode | Wardoff attempts to abort the shutdown while Windows still exposes an abortable timeout window | | Tray power actions | If validating tray Shutdown or Reboot directly, do it only in the VM | The action reaches Windows without risking the host workstation | +## Idle-power behavior and Windows limits + +This is the supported contract, not a claim that every row was physically tested. +`blocked` means automatic inactivity prevention while Windows honors the requests; +it never means a universal veto. Request visibility alone is not a sleep test. + +| Transition / environment | Previous thread execution-state backend | Power Request backend | Guarantee | +| --- | --- | --- | --- | +| Automatic idle sleep on S3 | Idle prevention | System-required request | **blocked**, subject to policy | +| Automatic display timeout | Display-idle prevention | Display-required request, paired with system-required | **blocked**, subject to policy | +| Automatic hibernation | No general S4 veto | No general S4 veto | **cannot guarantee**; preventing initial idle sleep can indirectly avoid a later S3-to-S4 transition | +| Explicit Start-menu Sleep | User request takes precedence | User request takes precedence | **unsupported** as a veto | +| Explicit Hibernate | No documented veto | No documented veto | **unsupported** as a veto | +| Power button configured for Sleep | No guaranteed veto | No guaranteed veto | **unsupported** as a veto | +| Lid configured for Sleep | No guaranteed veto | No guaranteed veto | **unsupported** as a veto | +| Traditional S3 | Idle prevention | Idle prevention | Requires a capable hardware test | +| Modern Standby / AC | No universal claim from the old backend | Same request pair, governed by Windows | **best effort**; validate on Modern Standby hardware | +| Modern Standby / DC | No unlimited guarantee established | System-required requests end five minutes after the sleep timeout expires | **best effort**, not five minutes after activating Block | +| Critical battery / thermal or other safety transition | No guarantee | No guarantee | **cannot guarantee** | +| Screen saver, lock or explicit display-off action | No guarantee | Outside the idle-display contract | **unsupported** | +| Policy ignores requests / existing request override | No guarantee | No guarantee | **cannot guarantee**; Wardoff does not change the policy | +| Away Mode | Not requested | Not requested | No **redirected/substituted** transition is claimed | + +### Microsoft evidence and rejected alternatives + +- [SetThreadExecutionState](https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setthreadexecutionstate) documents the inactivity flags, continuous lifetime, explicit-user-sleep limit and lack of screen-saver blocking. Periodic refresh does not turn this into a veto. +- [PowerSetRequest](https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-powersetrequest) defines the request types, explicit-sleep termination and Modern Standby battery timeout. Execution-required requests concern process lifetime and add no general sleep veto. +- [PowerCreateRequest](https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-powercreaterequest) and [PowerClearRequest](https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-powerclearrequest) define object ownership and balanced counts. [Process termination](https://learn.microsoft.com/en-us/windows/win32/procthread/terminating-a-process) closes process handles. The combination supports cleanup on crash for Wardoff's private, non-shared handle; verify this separately from orderly cleanup. +- [Allow away mode](https://learn.microsoft.com/en-us/windows-hardware/customize/power-settings/sleep-settings-allow-away-mode) can disable Away Mode even when requested. On eligible S3 systems it substitutes apparent sleep with video/audio off, not a veto; it has no Modern Standby coverage or documented general Hibernate veto. It is inappropriate for the single predictable Block/Allow contract and is not enabled. Reading capabilities and AC/DC policy could diagnose eligibility; Wardoff does not add that machinery for an excluded feature. +- [Allow system required requests](https://learn.microsoft.com/en-us/windows-hardware/customize/power-settings/sleep-settings-allow-system-required-requests) and [powercfg](https://learn.microsoft.com/en-us/windows-hardware/design/device-experiences/powercfg-command-line-options) explain policy rejection, request listing and overrides. A successful API call does not override those settings. +- [Hibernate idle timeout](https://learn.microsoft.com/en-us/windows-hardware/customize/power-settings/sleep-settings-hibernate-idle-timeout) describes the separate later hibernation transition. +- [PBT_APMQUERYSUSPEND](https://learn.microsoft.com/en-us/windows/win32/power/pbt-apmquerysuspend) and [PBT_APMQUERYSUSPENDFAILED](https://learn.microsoft.com/en-us/windows/win32/power/pbt-apmquerysuspendfailed) lost support in Vista; neither is a modern veto. +- [GetPwrCapabilities](https://learn.microsoft.com/en-us/windows/win32/api/powerbase/nf-powerbase-getpwrcapabilities) and [SYSTEM_POWER_CAPABILITIES](https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-system_power_capabilities) provide a documented capability query if later diagnostics need one. No architecture-specific backend is needed now: `powercfg /a` identifies the test machine, and the runtime uses the same two requests without a capability-detection subsystem. + +### Hardware procedure (manual, not CI) + +Use a prepared test PC, or a disposable VM only if it exposes the required power +states. Do not run disruptive tests on a workstation with unsaved work. Do not +enable Hibernate, change button/lid actions, set request overrides or edit a +power plan merely to make a row pass; use an already suitable test setup. + +1. Record Windows build, hardware, AC/DC source, binary hash and commit. Capture + `powercfg /a`, `/getactivescheme`, `/query`, `/requestsoverride` (without + arguments, read-only) and `/requests`. Record other applications' requests. +2. In Allow, confirm Wardoff remains alive with `layers.sleep=false` and no + Wardoff requests. Establish the existing idle/display behavior without + keyboard, mouse or remote-session activity that would reset the timers. +3. Enter Block. Confirm `layers.sleep=true` and the reason + `Wardoff Block mode: prevent idle sleep and automatic display timeout.` + in both requests. Wait past the configured idle/display timeouts; record + actual behavior, not just request listing. Repeat Block/Allow/Block. +4. Request Sleep from Windows, then wake the machine. Check mode, request pair + and logs; repeat for an already configured Sleep button, lid, and Hibernate. + Explicit sleep proceeding is expected, not a failed veto test. Record whether + the first observed resume renews the pair and whether subsequent notifications + leave exactly one pair. No real transition is proven by synthetic messages. +5. Test the existing tray Sleep/Hibernate flow from Block and from Allow. Block + should temporarily become Allow, then restore on wake; Allow should remain + Allow. Check `block_restored_after_wake` and request presence. Test an explicit + Allow command cancelling a pending restore where the timing permits it. +6. Include unattended/maintenance wake separately. This change retains the + existing resume notification semantics; it does not promise a new + presence-aware or display-aware policy. Record unexpected display behavior + rather than silently widening the resume system. +7. Repeat applicable cases on Modern Standby with AC and DC, including an AC/DC + change. Observe beyond the configured sleep timeout plus five minutes. Never + refresh in a loop to evade Windows' battery policy. Record missing hardware + as **not tested**, not as a pass or a claim that Modern Standby is unsupported. +8. Quit normally directly from Block, then separately terminate an owned test + process directly from Block. Verify its requests disappear in both cases. + Compare the power settings captured in step 1; no power setting should change. + Existing UpdateOrchestrator/autostart behavior is outside this sleep-layer + guarantee and should be tested separately in its documented environment. + +Power-request acquisition should also be exercised non-elevated; these APIs do +not document a special elevation requirement. Elevation needed to inspect +`powercfg /requests` is a diagnostic requirement, not a runtime requirement. + ## Acceptance checklist - [ ] `tests\smoke_test.ps1` passes, with any skips noted diff --git a/docs/OUTREACH_DRAFTS.md b/docs/OUTREACH_DRAFTS.md index eec7164..48b5b05 100644 --- a/docs/OUTREACH_DRAFTS.md +++ b/docs/OUTREACH_DRAFTS.md @@ -1,6 +1,14 @@ # Outreach drafts -Internal reference drafts for a small early-adopter outreach wave. Keep these aligned with `README.md`, `PLAN.md`, `docs/EARLY_ADOPTER_OUTREACH_PLAN.md`, and `docs/BUSINESS_MODEL.md`. Here's the project : https://github.com/RomainROCH/Wardoff +Internal reference drafts for a small early-adopter outreach wave. Keep these aligned with `README.md`, `PLAN.md`, `docs/MVP_VALIDATION_MATRIX.md`, and `docs/BUSINESS_MODEL.md`. Here's the project : https://github.com/RomainROCH/Wardoff + +## AlternativeTo (recommended copy; not published) + +**Short description:** Open-source Windows shutdown/reboot protection and idle-sleep prevention. + +**Description:** Wardoff is a lightweight native Windows utility written in Rust, with a tray icon and CLI for switching between Block and Allow. It combines interactive shutdown/sign-out protection, Windows Update reboot-task protection, best-effort remote shutdown aborts, and prevention of idle sleep and automatic display timeout. Power protection is subject to Windows policy: Wardoff does not veto explicit Sleep or Hibernate, configured lid/button actions, or safety transitions. Modern Standby battery limits also apply. The power-request layer makes no persistent changes to Windows power settings. + +**Tags to retain where available:** Windows, shutdown blocker, reboot prevention, prevent idle sleep, keep awake, system tray, command line, open source. The existing `prevent-sleep` tag remains justified for idle sleep when the description makes that scope explicit. Avoid tags claiming universal hibernation blocking or screen-saver prevention. This is a recommendation for the existing listing; no external listing was edited. ## r/rust @@ -8,7 +16,7 @@ Internal reference drafts for a small early-adopter outreach wave. Keep these al I built Wardoff as a Windows-only Rust utility for a narrow problem: keeping a machine in a visible Block state when I do not want Windows to shut down, sign out, sleep, or reboot unattended. -The current MVP is source-first and already has a tray app plus CLI. Today it does interactive shutdown/sign-out blocking, protects the `\Microsoft\Windows\UpdateOrchestrator\Reboot` task when that task exists, does best-effort remote shutdown abort polling, blocks sleep / hibernate / display idle, and writes structured JSONL logs. It is meant for technically comfortable Windows users, not as a polished mass-market app. +The current MVP is source-first and already has a tray app plus CLI. Today it does interactive shutdown/sign-out blocking, protects the `\Microsoft\Windows\UpdateOrchestrator\Reboot` task when that task exists, does best-effort remote shutdown abort polling, prevents idle sleep and automatic display timeout, subject to Windows power policy, and writes structured JSONL logs. It is meant for technically comfortable Windows users, not as a polished mass-market app. I am trying to keep the claims narrow and honest. I do **not** claim Wardoff stops `shutdown /t 0 /f`, and I do not present local `shutdown.exe` handling as solved. Right now the useful part is the conservative Windows behavior, the visible Block/Allow state, and the scriptable control surface. @@ -20,7 +28,7 @@ I would especially value feedback from Rust people who care about Windows API ed I built Wardoff because I wanted something simple and inspectable for the old problem of leaving a job running and coming back to find Windows restarted or went to sleep at the wrong time. -It is a Windows-only, source-first MIT project with a tray app and CLI. The current MVP gives me a visible Block/Allow state, autostart through Task Scheduler, machine-readable status output, and structured JSONL logs. It currently covers interactive shutdown/sign-out blocking, Update Orchestrator reboot-task protection when that scheduled task exists, best-effort remote shutdown abort polling, and sleep / hibernate / display-idle blocking. +It is a Windows-only, source-first MIT project with a tray app and CLI. The current MVP gives me a visible Block/Allow state, autostart through Task Scheduler, machine-readable status output, and structured JSONL logs. It currently covers interactive shutdown/sign-out blocking, Update Orchestrator reboot-task protection when that scheduled task exists, best-effort remote shutdown abort polling, and idle-sleep and automatic display-timeout prevention. I am deliberately keeping the wording narrow: I do **not** claim it stops `shutdown /t 0 /f`, and I am not presenting it as a polished enterprise product. The core behavior stays free and open; signed binaries are planned later as a convenience, not a paywall. @@ -42,6 +50,6 @@ If you try tools like this on Windows and have opinions about where the rough ed **Title:** `Show HN: Wardoff – Open-source Windows shutdown blocker in Rust` -I built Wardoff, a Windows-only Rust utility for keeping a machine in a visible Block state when I do not want shutdown, sign-out, sleep, hibernate, or certain reboot paths to interrupt work. The current MVP has a tray app, CLI control, structured JSONL logs, interactive shutdown/sign-out blocking, Update Orchestrator reboot-task protection when that task exists, remote shutdown abort polling, and sleep / hibernate / display-idle blocking. +I built Wardoff, a Windows-only Rust utility for keeping a machine in a visible Block state when I do not want supported shutdown, sign-out, idle-sleep, or reboot paths to interrupt work. The current MVP has a tray app, CLI control, structured JSONL logs, interactive shutdown/sign-out blocking, Update Orchestrator reboot-task protection when that task exists, remote shutdown abort polling, and idle-sleep and automatic display-timeout prevention. I am keeping the claims narrow: it is source-first today. An unsigned binary is also available on the Releases page. It is aimed at technically comfortable early adopters, and it does **not** claim to stop `shutdown /t 0 /f`. Source is MIT and free. Signed binaries are planned later as a convenience purchase, not a paywall. I would value feedback on Windows edge cases, docs clarity, and whether the current tray + CLI shape is the right one. Here's the project : https://github.com/RomainROCH/Wardoff diff --git a/docs/RELEASE_NOTES_DRAFT.md b/docs/RELEASE_NOTES_DRAFT.md index d049828..46e9ee3 100644 --- a/docs/RELEASE_NOTES_DRAFT.md +++ b/docs/RELEASE_NOTES_DRAFT.md @@ -28,7 +28,7 @@ ## Known limits - Layer 3 (UpdateOrchestrator) is automatically skipped on LTSC editions that lack the `UpdateOrchestrator\Reboot` scheduled task. This is normal. -- Sleep/hibernate blocking requires hardware support for S1-S3 standby states. Hyper-V VMs typically do not support these. +- Idle-power requests support S3 and Modern Standby, subject to Windows power policy. Explicit Sleep/Hibernate, lid and power-button actions are not vetoed. Hardware sleep behavior must be tested on a capable machine; VM request visibility alone is not proof. - `shutdown /t 0 /f` is not guaranteed blockable. This is a Windows limitation, not a Wardoff bug. - The product is source-first: no installer, no package manager distribution yet diff --git a/docs/WINDOWS_SHUTDOWN_LAYERS.md b/docs/WINDOWS_SHUTDOWN_LAYERS.md index b3f4108..27c82bd 100644 --- a/docs/WINDOWS_SHUTDOWN_LAYERS.md +++ b/docs/WINDOWS_SHUTDOWN_LAYERS.md @@ -12,7 +12,7 @@ Wardoff uses multiple layers because Windows shutdown, sign-out, reboot, sleep, | Layer 2 | Local `shutdown.exe` detection and best-effort abort | Implemented in code | Present in repo, but not promoted as a supported 0.1.0 headline feature | `src/blocker/local.rs`, `src/blocker/abort.rs`, `src/blocker/mod.rs` | Cannot honestly promise to stop `shutdown /t 0 /f` | | Layer 3 | Windows Update reboot-task protection | Implemented | Supported MVP behavior | `src/blocker/update.rs`, `src/blocker/mod.rs` | Requires elevation and periodic re-checks | | Layer 4 | Remote shutdown abort polling | Implemented | Supported MVP behavior | `src/blocker/remote.rs`, `src/blocker/abort.rs`, `src/blocker/mod.rs` | Only works while Windows still exposes an abortable window | -| Separate power-state guard | Sleep, hibernate, and display-idle prevention | Implemented | Supported MVP behavior | `src/blocker/sleep.rs`, `src/blocker/mod.rs` | Uses execution-state requests, not a shutdown veto | +| Separate power-state guard | Idle-sleep and automatic display-timeout prevention | Implemented | Subject to Windows power policy | `src/blocker/sleep.rs`, `src/blocker/mod.rs` | Power Requests do not veto explicit Sleep/Hibernate | `src/blocker/mod.rs` wires the runtime together through `BlockerCoordinator`, which activates Layer 2, Layer 3, Layer 4, sleep blocking, and finally Layer 1 when entering Block mode, then tears them down in reverse order when returning to Allow mode. @@ -24,7 +24,7 @@ Windows treats these as different categories of behavior: - local command-line shutdowns can start through `shutdown.exe` - Windows Update uses scheduled-task and service-driven restart flows - remote shutdown requests can create a pending shutdown that another process may still abort -- sleep and display-idle prevention are handled through execution-state hints instead of shutdown negotiation +- idle sleep and display-timeout prevention use Power Requests instead of shutdown negotiation Because of that split, one mechanism is never enough. @@ -194,23 +194,25 @@ Layer 4 helps only when: It does not rewind a shutdown after the no-return point. -## Separate power-state guard — Sleep, hibernate, and display idle +## Separate power-state guard — Idle sleep and display timeout This is not a shutdown layer, but it is a real part of the current runtime. Source: `src/blocker/sleep.rs` -- starts a dedicated worker thread in Block mode -- calls `SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED)` -- refreshes the request every 30 seconds -- clears the request with `SetThreadExecutionState(ES_CONTINUOUS)` when Block mode ends -- logs activation, refresh failures, and cleanup failures to the JSONL logger +- synchronously acquires system-required and display-required Power Requests on one private handle +- rolls back partial acquisition and reports API errors +- clears acquired requests and closes the handle when Block mode ends +- uses the existing resume callback to replace requests terminated by explicit sleep; tray actions retain their existing Allow-to-Block restoration +- logs acquisition and cleanup failures to the JSONL logger Current scope: -- sleep blocking -- hibernate blocking -- display-idle prevention +- idle-sleep and automatic display-timeout prevention, subject to Windows policy +- no veto over explicit Sleep, Hibernate, configured lid/button actions, or critical battery transitions +- no Away Mode, periodic refresh, persistent power-setting changes, or extra resume state machine + +See [architecture](ARCHITECTURE.md#idle-power-request-ownership-intentional-architecture-change) for ownership and [validation limits](MVP_VALIDATION_MATRIX.md#idle-power-behavior-and-windows-limits) for S3/Modern Standby behavior and hardware tests. ## Coordinator and lifecycle wiring @@ -239,7 +241,7 @@ Supported and safe to describe as current user-facing MVP behavior: - Layer 1 interactive shutdown/sign-out blocking - Layer 3 Update Orchestrator reboot-task protection - Layer 4 remote shutdown abort polling -- sleep, hibernate, and display-idle prevention +- idle-sleep and automatic display-timeout prevention, subject to Windows power policy Present in source, but documented more cautiously: diff --git a/src/blocker/mod.rs b/src/blocker/mod.rs index 870da04..bdc3b2b 100644 --- a/src/blocker/mod.rs +++ b/src/blocker/mod.rs @@ -23,7 +23,7 @@ pub mod local; pub mod remote; /// Scaffolding for Layer 1 interactive shutdown blocking. pub mod shutdown; -/// Scaffolding for SetThreadExecutionState-based sleep blocking. +/// Process-owned requests for idle-sleep and automatic display-timeout prevention. pub mod sleep; /// Scaffolding for Layer 3 UpdateOrchestrator reboot protection. pub mod update; @@ -50,6 +50,7 @@ pub struct LayerStatus { local_shutdown: bool, update: bool, remote: bool, + /// Whether Wardoff acquired its request pair, not a guarantee against explicit sleep. sleep: bool, } @@ -144,6 +145,14 @@ impl BlockerCoordinator { BLOCKED_EVENT_COUNT.load(Ordering::Relaxed) } + /// Renews idle-power requests only when Block remains the requested mode. + pub(crate) fn renew_sleep_requests_after_resume(&mut self) -> Result<(), String> { + if self.mode == BlockerMode::Block { + self.sleep_blocker.renew_after_resume()?; + } + Ok(()) + } + /// Disables the layered shutdown and sleep blocking scaffolding. pub fn deactivate(&mut self) -> Result<(), String> { self.set_mode(BlockerMode::Allow).map(|_| ()) diff --git a/src/blocker/shutdown.rs b/src/blocker/shutdown.rs index f1024ed..1d38410 100644 --- a/src/blocker/shutdown.rs +++ b/src/blocker/shutdown.rs @@ -87,15 +87,15 @@ impl ShutdownBlocker { // Since Windows 8, PBT_APMSUSPEND/PBT_APMRESUMEAUTOMATIC/PBT_APMRESUMESUSPEND are no longer // broadcast to top-level windows automatically; the recipient must register explicitly, // otherwise WM_POWERBROADCAST never reaches `shutdown_window_proc` for those events. - let suspend_resume_notification = - match register_suspend_resume_notification(session_window) { - Ok(handle) => Some(handle), - Err(error) => { - destroy_window(session_window); - destroy_window(message_window); - return Err(error); - } - }; + let suspend_resume_notification = match register_suspend_resume_notification(session_window) + { + Ok(handle) => Some(handle), + Err(error) => { + destroy_window(session_window); + destroy_window(message_window); + return Err(error); + } + }; Ok(Self { message_window, diff --git a/src/blocker/sleep.rs b/src/blocker/sleep.rs index 14c1abb..4e37afc 100644 --- a/src/blocker/sleep.rs +++ b/src/blocker/sleep.rs @@ -1,87 +1,82 @@ use crate::logger::{self, EventSource}; -use log::{error, info, warn}; -use std::sync::{ - atomic::{AtomicBool, Ordering}, - mpsc::{self, Receiver, RecvTimeoutError, Sender}, - Arc, -}; -use std::thread::{self, JoinHandle}; -use std::time::Duration; -use windows::core::{Error as WindowsError, Result as WindowsResult}; +use log::{info, warn}; +use windows::core::{w, PWSTR}; +use windows::Win32::Foundation::{CloseHandle, HANDLE}; use windows::Win32::System::Power::{ - SetThreadExecutionState, ES_CONTINUOUS, ES_DISPLAY_REQUIRED, ES_SYSTEM_REQUIRED, + PowerClearRequest, PowerCreateRequest, PowerRequestDisplayRequired, PowerRequestSystemRequired, + PowerSetRequest, POWER_REQUEST_TYPE, +}; +use windows::Win32::System::SystemServices::POWER_REQUEST_CONTEXT_VERSION; +use windows::Win32::System::Threading::{ + POWER_REQUEST_CONTEXT_SIMPLE_STRING, REASON_CONTEXT, REASON_CONTEXT_0, }; -const SLEEP_BLOCKER_THREAD_NAME: &str = "wardoff-sleep-blocker"; +const REQUEST_TYPES: [POWER_REQUEST_TYPE; 2] = + [PowerRequestSystemRequired, PowerRequestDisplayRequired]; -/// Coordinates SetThreadExecutionState-based sleep and hibernate blocking. +/// Owns idle-sleep/display-timeout requests, not a veto over explicit Sleep/Hibernate. +#[derive(Default)] pub struct SleepBlocker { - worker: Option, - active_state: Arc, + requests: PowerRequests, } impl SleepBlocker { - /// Starts sleep, hibernate, and display-idle blocking if it is not already active. + /// Acquires both requests synchronously, rolling back any partial acquisition. pub fn activate(&mut self) -> Result<(), String> { - if self.worker.is_some() { + if self.is_active() { return Ok(()); } - - let (stop_tx, stop_rx) = mpsc::channel(); - let active_state = Arc::clone(&self.active_state); - - match thread::Builder::new() - .name(SLEEP_BLOCKER_THREAD_NAME.to_string()) - .spawn(move || run_worker(active_state, stop_rx)) - { - Ok(join_handle) => { - self.worker = Some(SleepWorker { - stop_tx, - join_handle: Some(join_handle), - }); - Ok(()) - } - Err(error) => Err(format!( - "Sleep blocking could not start its worker thread: {error}" - )), + if let Err(error) = self.requests.activate() { + warn!("Could not acquire idle-power requests: {error}"); + logger::log_event("sleep_blocked", EventSource::Sleep, &error, false); + return Err(error); } + let message = "Wardoff acquired system/display Power Requests for idle-sleep and automatic display-timeout prevention. Windows policy and explicit Sleep/Hibernate can override this protection."; + info!("{message}"); + // Keep the existing event name for log consumers; success means request + // acquisition, not that an actual sleep transition was observed or vetoed. + logger::log_event("sleep_blocked", EventSource::Sleep, message, true); + Ok(()) } - /// Stops the worker thread and clears the execution-state request on the same thread. + /// Releases the requests and closes their private process-owned handle. pub fn deactivate(&mut self) { - self.begin_forced_shutdown_cleanup(); - - let Some(mut worker) = self.worker.take() else { + if self.requests.handle.is_none() { return; - }; - - if let Some(join_handle) = worker.join_handle.take() { - if join_handle.join().is_err() { - error!("Sleep-blocking worker thread panicked while stopping"); - logger::log_event( - "sleep_unblocked", - EventSource::Sleep, - "Sleep-blocking worker thread panicked while stopping.", - false, - ); + } + match self.requests.release() { + Ok(()) => logger::log_event( + "sleep_unblocked", + EventSource::Sleep, + "Wardoff released its idle-power requests and closed the request handle.", + true, + ), + Err(error) => { + warn!("Idle-power request cleanup failed: {error}"); + logger::log_event("sleep_unblocked", EventSource::Sleep, error, false); } } } - /// Requests the sleep-blocking worker to clear its execution-state request and exit. + /// Releases requests immediately without a worker thread to join. pub(crate) fn begin_forced_shutdown_cleanup(&mut self) { - let Some(worker) = self.worker.take() else { - return; - }; + self.deactivate(); + } - self.active_state.store(false, Ordering::Release); - let _ = worker.stop_tx.send(()); - self.worker = Some(worker); + /// Renews requests after the existing runtime resume notification. + pub(crate) fn renew_after_resume(&mut self) -> Result<(), String> { + // PowerSetRequest documents termination at user-initiated sleep entry: + // https://learn.microsoft.com/windows/win32/api/winbase/nf-winbase-powersetrequest + // The handle need not be invalid. Close the old object instead of + // incrementing possibly surviving counts or clearing already-ended ones. + // Tray power actions already reacquire via their Allow -> Block transition. + self.requests.close()?; + self.activate() } - /// Returns whether the sleep-blocking worker thread is currently active. + /// Reports acquisition of the pair; Windows policy may still ignore it. pub fn is_active(&self) -> bool { - self.active_state.load(Ordering::Acquire) + self.requests.is_active() } } @@ -91,131 +86,318 @@ impl Drop for SleepBlocker { } } -impl Default for SleepBlocker { +// Small private Win32 boundary for fault-injection tests. The production owner +// is monomorphized; there is no worker, shared state, or dynamic dispatch. +trait PowerApi { + type Handle: Copy; + fn create(&self) -> Result; + fn set(&self, handle: Self::Handle, kind: POWER_REQUEST_TYPE) -> Result<(), String>; + fn clear(&self, handle: Self::Handle, kind: POWER_REQUEST_TYPE) -> Result<(), String>; + fn close(&self, handle: Self::Handle) -> Result<(), String>; +} + +struct PowerRequests { + api: A, + handle: Option, + acquired: [bool; 2], +} + +impl Default for PowerRequests { fn default() -> Self { Self { - worker: None, - active_state: Arc::new(AtomicBool::new(false)), + api: A::default(), + handle: None, + acquired: [false; 2], } } } -/// Enables the execution state requests that keep the system awake while blocking is active. -pub fn enable_sleep_block() -> WindowsResult<()> { - let execution_state = ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED; +impl PowerRequests { + fn is_active(&self) -> bool { + self.handle.is_some() && self.acquired.iter().all(|acquired| *acquired) + } - if unsafe { SetThreadExecutionState(execution_state) }.0 == 0 { - Err(WindowsError::from_thread()) - } else { + fn activate(&mut self) -> Result<(), String> { + if self.is_active() { + return Ok(()); + } + let handle = self.api.create()?; + self.handle = Some(handle); + for (index, kind) in REQUEST_TYPES.into_iter().enumerate() { + if let Err(error) = self.api.set(handle, kind) { + return match self.release() { + Ok(()) => Err(error), + Err(cleanup_error) => Err(format!("{error}; cleanup: {cleanup_error}")), + }; + } + self.acquired[index] = true; + } Ok(()) } -} -/// Disables the execution state requests that were used to keep the system awake. -pub fn disable_sleep_block() -> WindowsResult<()> { - if unsafe { SetThreadExecutionState(ES_CONTINUOUS) }.0 == 0 { - Err(WindowsError::from_thread()) - } else { - Ok(()) + fn release(&mut self) -> Result<(), String> { + let mut errors = Vec::new(); + if let Some(handle) = self.handle { + for (kind, acquired) in REQUEST_TYPES.into_iter().zip(self.acquired) { + if acquired { + if let Err(error) = self.api.clear(handle, kind) { + errors.push(error); + } + } + } + } + // Closing still releases the object if a clear failed. Always attempt + // both types and CloseHandle, including after a partial acquisition. + if let Err(error) = self.close() { + errors.push(error); + } + if errors.is_empty() { + Ok(()) + } else { + Err(errors.join("; ")) + } } -} -/// Returns the interval used to refresh the execution state request. -pub fn refresh_interval() -> Duration { - Duration::from_secs(30) + fn close(&mut self) -> Result<(), String> { + self.acquired = [false; 2]; + match self.handle.take() { + Some(handle) => self.api.close(handle), + None => Ok(()), + } + } } -struct SleepWorker { - stop_tx: Sender<()>, - join_handle: Option>, +impl Drop for PowerRequests { + fn drop(&mut self) { + if let Err(error) = self.release() { + warn!("Idle-power request destruction failed: {error}"); + } + } } -struct ActiveStateGuard(Arc); +#[derive(Default)] +struct WindowsPowerApi; + +impl PowerApi for WindowsPowerApi { + type Handle = HANDLE; -impl ActiveStateGuard { - fn activate(active_state: Arc) -> Self { - active_state.store(true, Ordering::Release); - Self(active_state) + fn create(&self) -> Result { + let reason = w!("Wardoff Block mode: prevent idle sleep and automatic display timeout."); + let context = REASON_CONTEXT { + Version: POWER_REQUEST_CONTEXT_VERSION, + Flags: POWER_REQUEST_CONTEXT_SIMPLE_STRING, + Reason: REASON_CONTEXT_0 { + // Microsoft explicitly permits read-only strings here; the API + // only reads this static, NUL-terminated UTF-16 reason. + SimpleReasonString: PWSTR(reason.as_ptr().cast_mut()), + }, + }; + unsafe { PowerCreateRequest(&context) } + .map_err(|error| format!("PowerCreateRequest failed: {error}")) } -} -impl Drop for ActiveStateGuard { - fn drop(&mut self) { - self.0.store(false, Ordering::Release); + fn set(&self, handle: HANDLE, kind: POWER_REQUEST_TYPE) -> Result<(), String> { + unsafe { PowerSetRequest(handle, kind) } + .map_err(|error| format!("PowerSetRequest({kind:?}) failed: {error}")) + } + + fn clear(&self, handle: HANDLE, kind: POWER_REQUEST_TYPE) -> Result<(), String> { + unsafe { PowerClearRequest(handle, kind) } + .map_err(|error| format!("PowerClearRequest({kind:?}) failed: {error}")) + } + + fn close(&self, handle: HANDLE) -> Result<(), String> { + unsafe { CloseHandle(handle) }.map_err(|error| format!("CloseHandle failed: {error}")) } } -fn run_worker(active_state: Arc, stop_rx: Receiver<()>) { - if let Err(error) = enable_sleep_block() { - warn!( - "Sleep blocking could not activate SetThreadExecutionState; skipping sleep, hibernate, and display-idle protection: {error}" - ); - logger::log_event( - "sleep_blocked", - EventSource::Sleep, - format!( - "Wardoff could not activate SetThreadExecutionState and skipped sleep, hibernate, and display-idle blocking: {error}" - ), - false, - ); - return; - } - - let _active_guard = ActiveStateGuard::activate(active_state); - info!( - "Sleep, hibernate, and display-idle blocking are active and will refresh SetThreadExecutionState every {} seconds while Block mode is active.", - refresh_interval().as_secs() - ); - logger::log_event( - "sleep_blocked", - EventSource::Sleep, - format!( - "Wardoff activated SetThreadExecutionState refresh every {} seconds to block sleep, hibernate, and display idle.", - refresh_interval().as_secs() - ), - true, - ); - let mut stopped_cleanly = true; - - loop { - match stop_rx.recv_timeout(refresh_interval()) { - Ok(()) | Err(RecvTimeoutError::Disconnected) => break, - Err(RecvTimeoutError::Timeout) => { - if let Err(error) = enable_sleep_block() { - warn!( - "Sleep blocking stopped after its SetThreadExecutionState refresh failed: {error}" - ); - logger::log_event( - "sleep_blocked", - EventSource::Sleep, - format!( - "Wardoff stopped refreshing SetThreadExecutionState after an error: {error}" - ), - false, - ); - stopped_cleanly = false; - break; - } +#[cfg(test)] +mod tests { + use super::*; + use std::cell::RefCell; + use std::collections::BTreeMap; + use std::rc::Rc; + + #[derive(Clone, Copy, Debug, PartialEq)] + enum Call { + Create, + Set(i32), + Clear(i32), + Close, + } + + #[derive(Default)] + struct State { + calls: Vec, + failures: Vec, + next_handle: u32, + live: BTreeMap, + } + + #[derive(Clone, Default)] + struct FakeApi(Rc>); + + impl FakeApi { + fn record(&self, call: Call) -> Result<(), String> { + let mut state = self.0.borrow_mut(); + state.calls.push(call); + if state.failures.contains(&call) { + Err(format!("injected {call:?} failure")) + } else { + Ok(()) } } } - if let Err(error) = disable_sleep_block() { - warn!( - "Sleep blocking could not clear its SetThreadExecutionState request during shutdown: {error}" - ); - logger::log_event( - "sleep_unblocked", - EventSource::Sleep, - format!("Wardoff could not clear SetThreadExecutionState during shutdown: {error}"), - false, - ); - } else if stopped_cleanly { - logger::log_event( - "sleep_unblocked", - EventSource::Sleep, - "Wardoff cleared SetThreadExecutionState as Block mode ended.", - true, - ); + impl PowerApi for FakeApi { + type Handle = u32; + fn create(&self) -> Result { + self.record(Call::Create)?; + let mut state = self.0.borrow_mut(); + state.next_handle += 1; + let handle = state.next_handle; + state.live.insert(handle, [0; 2]); + Ok(handle) + } + fn set(&self, handle: u32, kind: POWER_REQUEST_TYPE) -> Result<(), String> { + self.record(Call::Set(kind.0))?; + self.0.borrow_mut().live.get_mut(&handle).unwrap()[kind.0 as usize] += 1; + Ok(()) + } + fn clear(&self, handle: u32, kind: POWER_REQUEST_TYPE) -> Result<(), String> { + self.record(Call::Clear(kind.0))?; + let mut state = self.0.borrow_mut(); + let count = &mut state.live.get_mut(&handle).unwrap()[kind.0 as usize]; + assert!(*count > 0, "must not clear an unacquired request"); + *count -= 1; + Ok(()) + } + fn close(&self, handle: u32) -> Result<(), String> { + self.record(Call::Close)?; + assert!(self.0.borrow_mut().live.remove(&handle).is_some()); + Ok(()) + } + } + + fn requests() -> (PowerRequests, FakeApi) { + let requests = PowerRequests::::default(); + let api = requests.api.clone(); + (requests, api) + } + + #[test] + fn activation_reports_an_acquired_request_before_returning() { + // Non-disruptive Win32 integration: no sleep, policy writes or elevation. + let mut blocker = SleepBlocker::default(); + for _ in 0..10 { + blocker + .activate() + .expect("power requests should be available"); + assert!(blocker.is_active()); + blocker + .renew_after_resume() + .expect("renew the owned requests"); + assert!(blocker.is_active()); + blocker.deactivate(); + assert!(!blocker.is_active()); + } + } + + #[test] + fn repeated_block_allow_cycles_balance_counts_and_close_objects() { + let (mut requests, api) = requests(); + for _ in 0..100 { + requests.activate().unwrap(); + requests.activate().unwrap(); + assert!(requests.is_active()); + assert_eq!( + api.0.borrow().live.values().copied().collect::>(), + vec![[1, 1]] + ); + requests.release().unwrap(); + requests.release().unwrap(); + assert!(!requests.is_active()); + assert!(api.0.borrow().live.is_empty()); + } + } + + #[test] + fn failed_creation_does_not_claim_protection_or_close_an_invalid_handle() { + let (mut requests, api) = requests(); + api.0.borrow_mut().failures = vec![Call::Create]; + assert!(requests.activate().is_err()); + assert!(!requests.is_active()); + drop(requests); + assert_eq!(api.0.borrow().calls, vec![Call::Create]); + } + + #[test] + fn either_set_failure_rolls_back_only_acquired_requests_and_can_retry() { + for kind in REQUEST_TYPES { + let (mut requests, api) = requests(); + api.0.borrow_mut().failures = vec![Call::Set(kind.0)]; + assert!(requests.activate().is_err()); + assert!(!requests.is_active()); + assert!(api.0.borrow().live.is_empty()); + assert!(!api.0.borrow().calls.contains(&Call::Clear(kind.0))); + api.0.borrow_mut().failures.clear(); + requests.activate().unwrap(); + assert!(requests.is_active()); + drop(requests); + assert!(api.0.borrow().live.is_empty()); + } + } + + #[test] + fn cleanup_continues_after_clear_errors_and_reports_each_error() { + let (mut requests, api) = requests(); + requests.activate().unwrap(); + api.0.borrow_mut().failures = vec![Call::Clear(1), Call::Clear(0)]; + let error = requests.release().unwrap_err(); + assert!(error.contains("Clear(1)")); + assert!(error.contains("Clear(0)")); + assert!(api.0.borrow().live.is_empty()); + assert!(!requests.is_active()); + } + + #[test] + fn partial_acquisition_preserves_original_and_cleanup_errors() { + let (mut requests, api) = requests(); + api.0.borrow_mut().failures = vec![Call::Set(0), Call::Clear(1)]; + let error = requests.activate().unwrap_err(); + assert!(error.contains("Set(0)")); + assert!(error.contains("Clear(1)")); + assert!(api.0.borrow().live.is_empty()); + } + + #[test] + fn drop_releases_both_requests_without_explicit_deactivation() { + let (mut requests, api) = requests(); + requests.activate().unwrap(); + drop(requests); + assert!(api.0.borrow().live.is_empty()); + assert!(api + .0 + .borrow() + .calls + .ends_with(&[Call::Clear(1), Call::Clear(0), Call::Close])); + } + + #[test] + fn resume_replacement_handles_ended_or_surviving_counts_without_accumulation() { + for counts in [[0, 0], [1, 1]] { + let (mut requests, api) = requests(); + requests.activate().unwrap(); + *api.0.borrow_mut().live.values_mut().next().unwrap() = counts; + // This validates ownership, not real Windows suspend behavior. + for _ in 0..2 { + requests.close().unwrap(); + requests.activate().unwrap(); + assert_eq!( + api.0.borrow().live.values().copied().collect::>(), + vec![[1, 1]] + ); + } + } } } diff --git a/src/cli.rs b/src/cli.rs index ecdd2fc..108b5bb 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -41,7 +41,7 @@ enum CliAutostartState { name = "wardoff", author = "Romain ROCH", version = env!("CARGO_PKG_VERSION"), - about = "Open-source Windows shutdown/reboot/sleep blocker" + about = "Open-source Windows shutdown/reboot protection and idle-sleep prevention" )] pub struct WardoffCli { /// Enables blocking mode for the current session without showing a tray icon. @@ -236,7 +236,7 @@ mod tests { assert_eq!(error.kind(), ErrorKind::DisplayHelp); assert!(error .to_string() - .contains("Open-source Windows shutdown/reboot/sleep blocker")); + .contains("Open-source Windows shutdown/reboot protection and idle-sleep prevention")); } #[test] diff --git a/src/logger/mod.rs b/src/logger/mod.rs index 6ce6785..0823db1 100644 --- a/src/logger/mod.rs +++ b/src/logger/mod.rs @@ -43,7 +43,7 @@ pub enum EventSource { Remote, /// The Layer 1 shutdown blocker emitted the event. Shutdown, - /// The sleep and hibernate blocker emitted the event. + /// The idle-sleep/display-timeout request owner emitted the event. Sleep, /// The tray surface emitted the event. Tray, diff --git a/src/main.rs b/src/main.rs index 2ec3866..ec30e66 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1090,6 +1090,12 @@ impl Application { } } blocker::shutdown::PowerBroadcastEvent::Resume => { + // The tray path is still Allow here and reacquires through its + // existing restore. Renew only a runtime that stayed in Block. + if let Err(error) = self.blocker_coordinator.renew_sleep_requests_after_resume() { + warn!("Could not renew idle-power requests after resume: {error}"); + logger::log_event("sleep_resume_failed", EventSource::Sleep, error, false); + } self.restore_block_after_wake_if_needed(); } } diff --git a/tests/smoke_test.ps1 b/tests/smoke_test.ps1 index 5e608f5..f5fc956 100644 --- a/tests/smoke_test.ps1 +++ b/tests/smoke_test.ps1 @@ -12,14 +12,18 @@ $repoRoot = [System.IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..')) $logPath = Join-Path (Join-Path $env:LOCALAPPDATA 'Wardoff') 'logs\wardoff.jsonl' function Resolve-BinaryPath { - $defaultBinaryPath = Join-Path $repoRoot 'target\release\wardoff.exe' + $releasePath = 'release\wardoff.exe' + if (-not [string]::IsNullOrWhiteSpace($env:CARGO_BUILD_TARGET)) { + $releasePath = Join-Path $env:CARGO_BUILD_TARGET $releasePath + } + $defaultBinaryPath = Join-Path (Join-Path $repoRoot 'target') $releasePath try { $metadataJson = & cargo metadata --format-version 1 --no-deps 2>$null if (-not [string]::IsNullOrWhiteSpace($metadataJson)) { $metadata = $metadataJson | ConvertFrom-Json -ErrorAction Stop if (-not [string]::IsNullOrWhiteSpace($metadata.target_directory)) { - return Join-Path $metadata.target_directory 'release\wardoff.exe' + return Join-Path $metadata.target_directory $releasePath } } } @@ -721,7 +725,7 @@ try { Invoke-TestCase 'Start-Process launches wardoff --block --hide in the background' { Stop-RepoWardoffProcesses $script:InitialLogLineCount = @(Get-StructuredLogLines).Count - $script:BackgroundProcess = Start-Process -FilePath $binaryPath -ArgumentList @('--block', '--hide') -WorkingDirectory $repoRoot -PassThru + $script:BackgroundProcess = Start-Process -FilePath $binaryPath -ArgumentList @('--block', '--hide') -WorkingDirectory $repoRoot -WindowStyle Hidden -PassThru Start-Sleep -Seconds 2 $script:BackgroundProcess.Refresh() Assert-Condition (-not $script:BackgroundProcess.HasExited) "Wardoff exited early with code $($script:BackgroundProcess.ExitCode)." @@ -734,6 +738,7 @@ try { Assert-Condition ($result.ExitCode -eq 0) "wardoff --status exited with code $($result.ExitCode) while Wardoff was running." Assert-Condition ($status.state -eq 'block') "wardoff --status returned state '$($status.state)' instead of 'block'." + Assert-Condition ($status.layers.sleep -eq $true) 'Block mode did not acquire both idle-power requests.' Assert-Condition ($null -ne $status.layers) 'wardoff --status did not include a layers object while Wardoff was running.' Assert-Condition ($null -ne $status.layers.local_shutdown) 'wardoff --status did not include the layers.local_shutdown field.' Assert-Condition ($status.layers.local_shutdown -is [bool]) 'wardoff --status returned a non-boolean layers.local_shutdown field.' @@ -760,7 +765,8 @@ try { if ($powercfgRequestsCheck.State -eq 'testable') { Invoke-TestCase 'powercfg /requests mentions Wardoff while blocking is active' { $result = $powercfgRequestsCheck.Result - Assert-Condition ($result.Output -match '(?i)wardoff') 'powercfg /requests did not mention Wardoff.' + $reason = 'Wardoff Block mode: prevent idle sleep and automatic display timeout.' + Assert-Condition ([regex]::Matches($result.Output, [regex]::Escape($reason)).Count -eq 2) 'Expected the Wardoff reason once for SYSTEM and once for DISPLAY.' } } elseif ($powercfgRequestsCheck.State -eq 'skip') { @@ -852,18 +858,45 @@ try { Skip-TestCase 'wardoff --autostart on creates the Wardoff task and --autostart off removes it' $adminOnlyMessage } - Invoke-TestCase 'The background Wardoff instance can be stopped and cleaned up' { + Invoke-TestCase 'Allow releases idle-power requests without stopping the runtime' { $repoProcesses = @(Get-RepoWardoffProcesses) - Assert-Condition ($repoProcesses.Count -gt 0) 'No background Wardoff process was running to stop.' + Assert-Condition ($repoProcesses.Count -eq 1) 'Expected one background Wardoff process.' + $allowResult = Invoke-ExternalCommand -FilePath $binaryPath -Arguments @('--allow') + Assert-Condition ($allowResult.ExitCode -eq 0) "wardoff --allow exited with code $($allowResult.ExitCode)." + $statusResult = Invoke-ExternalCommand -FilePath $binaryPath -Arguments @('--status') + Assert-Condition ($statusResult.ExitCode -eq 0) 'Status failed after Allow.' + $status = $statusResult.Output | ConvertFrom-Json + Assert-Condition ($status.state -eq 'allow' -and $status.layers.sleep -eq $false) 'Allow did not report released idle-power requests.' + $remaining = @(Get-RepoWardoffProcesses) + Assert-Condition ($remaining.Count -eq 1 -and $remaining[0].ProcessId -eq $repoProcesses[0].ProcessId) 'Allow replaced or stopped the runtime.' + } - try { - $allowResult = Invoke-ExternalCommand -FilePath $binaryPath -Arguments @('--allow') - Assert-Condition ($allowResult.ExitCode -eq 0) "wardoff --allow exited with code $($allowResult.ExitCode)." - } - catch { + $allowRequestsCheck = Resolve-PowercfgRequestsCheck + if ($allowRequestsCheck.State -eq 'testable') { + Invoke-TestCase 'powercfg /requests has no Wardoff entry in Allow mode' { + Assert-Condition ($allowRequestsCheck.Result.Output -notmatch '(?i)wardoff') 'Wardoff requests remained after Allow.' } + } + elseif ($allowRequestsCheck.State -eq 'skip') { + Skip-TestCase 'powercfg /requests has no Wardoff entry in Allow mode' $allowRequestsCheck.SkipReason + } + else { + Add-TestResult -Status 'FAIL' -Description 'powercfg /requests has no Wardoff entry in Allow mode' -Details $allowRequestsCheck.FailureMessage + } - Stop-RepoWardoffProcesses + Invoke-TestCase 'Repeated Block reacquires idle-power requests before abrupt process termination' { + foreach ($iteration in 1..2) { + $blockResult = Invoke-ExternalCommand -FilePath $binaryPath -Arguments @('--block') + Assert-Condition ($blockResult.ExitCode -eq 0) 'Block failed after Allow.' + $activeStatus = Wait-ForActiveWardoffStatus + Assert-Condition ($activeStatus.Status.layers.sleep -eq $true) 'Block did not reacquire idle-power requests.' + } + # Deliberately do not call the normal cleanup helper: it sends --allow first. + $repoProcesses = @(Get-RepoWardoffProcesses) + Assert-Condition ($repoProcesses.Count -eq 1) 'Expected one owned runtime to terminate.' + $ownedProcess = Get-Process -Id $repoProcesses[0].ProcessId -ErrorAction Stop + Stop-Process -InputObject $ownedProcess -Force -ErrorAction Stop + Assert-Condition ($ownedProcess.WaitForExit(5000)) 'The terminated runtime did not exit within five seconds.' Assert-Condition (@(Get-RepoWardoffProcesses).Count -eq 0) 'Wardoff was still running after cleanup.' } From 36e19bb0a0c9ed9eb7b0e660e9bae48c28d38448 Mon Sep 17 00:00:00 2001 From: RomainROCH Date: Sat, 3 Oct 2026 17:40:17 +0200 Subject: [PATCH 03/15] fix: use identification tokens for autostart access checks --- src/autostart.rs | 53 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/src/autostart.rs b/src/autostart.rs index 01ef1e7..b38401d 100644 --- a/src/autostart.rs +++ b/src/autostart.rs @@ -21,7 +21,7 @@ use windows::Win32::Security::Authorization::{ ConvertSidToStringSidW, GetNamedSecurityInfoW, SE_FILE_OBJECT, }; use windows::Win32::Security::{ - AccessCheck, DuplicateTokenEx, GetTokenInformation, LookupAccountSidW, SecurityImpersonation, + AccessCheck, DuplicateTokenEx, GetTokenInformation, LookupAccountSidW, SecurityIdentification, TokenImpersonation, TokenLinkedToken, TokenUser, DACL_SECURITY_INFORMATION, GENERIC_MAPPING, GROUP_SECURITY_INFORMATION, OWNER_SECURITY_INFORMATION, PRIVILEGE_SET, PSECURITY_DESCRIPTOR, PSID, TOKEN_DUPLICATE, TOKEN_IMPERSONATE, TOKEN_LINKED_TOKEN, TOKEN_QUERY, TOKEN_USER, @@ -440,12 +440,21 @@ fn current_interactive_medium_token_for_access_check() -> Result Result { + unsafe { let mut impersonation_token = HANDLE::default(); DuplicateTokenEx( - linked_token.0, + source_token, TOKEN_QUERY, None, - SecurityImpersonation, + // AccessCheck only inspects the user's permissions. A linked UAC + // token can be identification-only and cannot be promoted to an + // impersonation level that would let us act as that user. + SecurityIdentification, TokenImpersonation, &mut impersonation_token, ) @@ -737,3 +746,41 @@ impl Drop for LocalAllocatedWideString { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn access_check_accepts_an_identification_level_source_token() { + let process_token = open_current_process_token( + TOKEN_QUERY | TOKEN_DUPLICATE, + "prepare the identification-token regression test", + ) + .expect("open test process token"); + let mut source_token = HANDLE::default(); + unsafe { + DuplicateTokenEx( + process_token.0, + TOKEN_QUERY | TOKEN_DUPLICATE, + None, + SecurityIdentification, + TokenImpersonation, + &mut source_token, + ) + } + .expect("create an identification-level source token"); + let source_token = HandleGuard(source_token); + + let access_token = duplicate_access_check_token(source_token.0) + .expect("identification-level tokens must support access checks"); + let executable = std::env::current_exe().expect("locate the running test executable"); + assert!(token_has_path_access( + access_token.0, + &executable, + FILE_GENERIC_READ.0, + "read the test executable", + ) + .expect("evaluate file access with the identification-level token")); + } +} From cb8fcf63dd7c2b3cd4cfcd645836336ea87f7125 Mon Sep 17 00:00:00 2001 From: RomainROCH Date: Sat, 3 Oct 2026 17:42:06 +0200 Subject: [PATCH 04/15] test: skip update protection when the reboot task is absent --- tests/smoke_test.ps1 | 46 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/tests/smoke_test.ps1 b/tests/smoke_test.ps1 index f5fc956..da45c0d 100644 --- a/tests/smoke_test.ps1 +++ b/tests/smoke_test.ps1 @@ -41,6 +41,35 @@ function Test-IsAdministrator { return $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) } +function Test-RebootTaskPresent { + $scheduler = $null + $folder = $null + $task = $null + try { + $scheduler = New-Object -ComObject 'Schedule.Service' + $scheduler.Connect() + $folder = $scheduler.GetFolder('\Microsoft\Windows\UpdateOrchestrator') + $task = $folder.GetTask('Reboot') + return $true + } + catch { + # Match the runtime's missing-file, missing-path and missing-object cases. + # Permission and other scheduler errors must still fail validation. + $hresult = $_.Exception.GetBaseException().HResult + if ($hresult -in @(-2147024894, -2147024893, -2147023728)) { + return $false + } + throw + } + finally { + foreach ($comObject in @($task, $folder, $scheduler)) { + if ($null -ne $comObject) { + [void][Runtime.InteropServices.Marshal]::ReleaseComObject($comObject) + } + } + } +} + $isAdmin = Test-IsAdministrator if (-not $isAdmin) { @@ -798,9 +827,20 @@ try { } if ($isAdmin) { - Invoke-TestCase 'wardoff --status reports Layer 3 UpdateOrchestrator protection as active when run as admin' { - $activeStatus = Wait-ForActiveWardoffStatus - Assert-Condition ($activeStatus.Status.layers.update -eq $true) 'wardoff --status did not report layers.update=true in an elevated session.' + $layer3Description = 'wardoff --status reports Layer 3 UpdateOrchestrator protection as active when run as admin' + try { + if (Test-RebootTaskPresent) { + Invoke-TestCase $layer3Description { + $activeStatus = Wait-ForActiveWardoffStatus + Assert-Condition ($activeStatus.Status.layers.update -eq $true) 'wardoff --status did not report layers.update=true in an elevated session.' + } + } + else { + Skip-TestCase $layer3Description 'The UpdateOrchestrator Reboot task is absent on this Windows installation.' + } + } + catch { + Add-TestResult -Status 'FAIL' -Description $layer3Description -Details $_.Exception.Message } Invoke-TestCase 'wardoff --status reports Layer 4 AbortSystemShutdown protection as active when run as admin' { From 60399554db753cb010a6770e24ec1b8fa2dcae93 Mon Sep 17 00:00:00 2001 From: RomainROCH Date: Sun, 4 Oct 2026 18:50:52 +0200 Subject: [PATCH 05/15] fix: bound control pipe request reads --- docs/SECURITY_AUDIT.md | 19 +- src/ipc.rs | 117 ++++++++++-- tests/ipc_request_limits.rs | 358 ++++++++++++++++++++++++++++++++++++ 3 files changed, 466 insertions(+), 28 deletions(-) create mode 100644 tests/ipc_request_limits.rs diff --git a/docs/SECURITY_AUDIT.md b/docs/SECURITY_AUDIT.md index f6656b9..0048553 100644 --- a/docs/SECURITY_AUDIT.md +++ b/docs/SECURITY_AUDIT.md @@ -7,7 +7,7 @@ ## Summary -Wardoff is materially safer than it was at the previous audit point and is acceptable for distribution to source-first technical early adopters on self-administered machines, but it is still **not yet hardened** for a hostile local-attacker model or multi-user deployment scenarios. The five original pre-release blockers around writable-path autostart, log-directory symlink abuse, named-pipe squatting, cross-session mutex scope, and spoofed `WM_ENDSESSION` teardown are resolved in the current `dev` state. The remaining concerns are same-session/local hardening debt: residual log file targeting, same-session mutex denial of service, the status pipe's default DACL, and the control pipe's unbounded single-client request read path. +Wardoff is materially safer than it was at the previous audit point and is acceptable for distribution to source-first technical early adopters on self-administered machines, but it is still **not yet hardened** for a hostile local-attacker model or multi-user deployment scenarios. The five original pre-release blockers around writable-path autostart, log-directory symlink abuse, named-pipe squatting, cross-session mutex scope, and spoofed `WM_ENDSESSION` teardown are resolved in the current `dev` state. The remaining concerns are same-session/local hardening debt: residual log file targeting, same-session mutex denial of service, and the status pipe's default DACL. The follow-up below records the bounded control-request reader. ## Findings @@ -91,15 +91,16 @@ Wardoff is materially safer than it was at the previous audit point and is accep - **Fix status:** **still open** - **Recommended fix:** Apply an explicit reviewed security descriptor to the status pipe as well. -### [MEDIUM] Control pipe request handling still allows single-client read denial of service +### [INFORMATIONAL] Fixed: control pipe request reads are bounded -- **Location:** `src/ipc.rs:430-537`, `src/ipc.rs:899-914` +- **Location:** `src/ipc.rs` (`read_request`, `read_request_until_deadline`), `tests/ipc_request_limits.rs` - **Attack surface:** Named pipe security -- **Description:** The control server still accepts one client at a time and blocks on `BufRead::read_line` with no explicit size cap or read timeout. A client that connects and never finishes a line can still stall command processing. -- **Exploitability:** Any local client that can connect to the control pipe can deny service to other control requests for the duration of that stalled read. -- **Risk for early-adopter release:** **fix before broader distribution** -- **Fix status:** **still open** -- **Recommended fix:** Add request-size caps plus read timeouts or a more defensive multi-client/overlapped handling model. +- **Description:** The original single-client server blocked on an unbounded `BufRead::read_line`. The 2026-10-04 follow-up limits each control frame to 4096 wire bytes including its required newline and applies a one-second total read deadline. Nonblocking Win32 byte reads distinguish temporary lack of data from EOF; incoming fragments do not renew the deadline. Rejected connections retain nonblocking writes so a client refusing its error reply cannot stall the next accept. +- **Exploitability:** One incomplete client can occupy the serial reader only until its deadline, rather than indefinitely. Repeated connections and existing UI/valid-response waits remain outside this targeted fix; this is not a general hostile-client or multi-user hardening claim. +- **Risk for early-adopter release:** **informational for the original unbounded-read finding** +- **Fix status:** **resolved in source** +- **Validation:** An isolated test runs the production control/status loops on process-unique endpoints with an inert UI consumer. It covers valid and malformed JSON, empty input, UTF-8 errors, size boundaries and fragmented oversized input, missing newline, silent and trickling clients, disconnect/recovery, independent status response before the control deadline, and orderly shutdown while a client is incomplete. No power protections or autostart changes are required. Run `cargo test --test ipc_request_limits -- --nocapture`. +- **Recommended follow-up:** Retain this regression test and validate supported Windows environments before broader distribution. The same-user/elevation policy and status-pipe ACL are unchanged. ### [MEDIUM] Scheduled-task ACL hardening is still not explicit @@ -154,4 +155,4 @@ Additional notes: ## Conclusion -Wardoff is acceptable for distribution to source-first technical early adopters on self-administered machines. The five original pre-release blockers (writable-path autostart, log-directory symlink abuse, named-pipe squatting, cross-session mutex, WM_ENDSESSION spoofing) are resolved. Known remaining items (same-session mutex DoS, status pipe default DACL, control pipe unbounded read, residual log file targeting) are local hardening debt against same-session adversaries, not exploitable privilege escalation. Wardoff is not yet hardened against a hostile local-attacker model or multi-user deployment scenarios. +Wardoff is acceptable for distribution to source-first technical early adopters on self-administered machines. The five original pre-release blockers (writable-path autostart, log-directory symlink abuse, named-pipe squatting, cross-session mutex, WM_ENDSESSION spoofing) are resolved, and the follow-up bounds the control-request read. Known remaining items (same-session mutex DoS, status pipe default DACL, residual log file targeting) are local hardening debt against same-session adversaries, not exploitable privilege escalation. Wardoff is not yet hardened against a hostile local-attacker model or multi-user deployment scenarios. diff --git a/src/ipc.rs b/src/ipc.rs index d335da8..a71a7b5 100644 --- a/src/ipc.rs +++ b/src/ipc.rs @@ -7,19 +7,19 @@ use serde::{Deserialize, Serialize}; use std::fs::{File, OpenOptions}; use std::io::{BufRead, BufReader, Write}; use std::mem::size_of; -use std::os::windows::io::{FromRawHandle, RawHandle}; +use std::os::windows::io::{AsRawHandle, FromRawHandle, RawHandle}; use std::sync::{ atomic::{AtomicBool, Ordering}, mpsc::{self, Sender}, Arc, }; use std::thread::{self, JoinHandle}; -use std::time::Duration; +use std::time::{Duration, Instant}; use windows::core::{Error as WindowsError, PCWSTR, PWSTR}; use windows::Win32::Foundation::{ - CloseHandle, GetLastError, LocalFree, ERROR_ACCESS_DENIED, ERROR_FILE_NOT_FOUND, - ERROR_INSUFFICIENT_BUFFER, ERROR_PATH_NOT_FOUND, ERROR_PIPE_BUSY, ERROR_PIPE_CONNECTED, HANDLE, - HLOCAL, LPARAM, WPARAM, + CloseHandle, GetLastError, LocalFree, ERROR_ACCESS_DENIED, ERROR_BROKEN_PIPE, + ERROR_FILE_NOT_FOUND, ERROR_INSUFFICIENT_BUFFER, ERROR_NO_DATA, ERROR_PATH_NOT_FOUND, + ERROR_PIPE_BUSY, ERROR_PIPE_CONNECTED, HANDLE, HLOCAL, LPARAM, WPARAM, }; use windows::Win32::Security::Authorization::{ ConvertSidToStringSidW, ConvertStringSecurityDescriptorToSecurityDescriptorW, SDDL_REVISION_1, @@ -29,12 +29,12 @@ use windows::Win32::Security::{ TOKEN_USER, }; use windows::Win32::Storage::FileSystem::{ - FILE_FLAGS_AND_ATTRIBUTES, FILE_FLAG_FIRST_PIPE_INSTANCE, PIPE_ACCESS_DUPLEX, + ReadFile, FILE_FLAGS_AND_ATTRIBUTES, FILE_FLAG_FIRST_PIPE_INSTANCE, PIPE_ACCESS_DUPLEX, PIPE_ACCESS_OUTBOUND, }; use windows::Win32::System::Pipes::{ - ConnectNamedPipe, CreateNamedPipeW, PIPE_READMODE_MESSAGE, PIPE_REJECT_REMOTE_CLIENTS, - PIPE_TYPE_MESSAGE, PIPE_WAIT, + ConnectNamedPipe, CreateNamedPipeW, SetNamedPipeHandleState, PIPE_NOWAIT, PIPE_READMODE_BYTE, + PIPE_READMODE_MESSAGE, PIPE_REJECT_REMOTE_CLIENTS, PIPE_TYPE_MESSAGE, PIPE_WAIT, }; use windows::Win32::System::Threading::{GetCurrentProcess, OpenProcessToken}; use windows::Win32::UI::WindowsAndMessaging::{PostThreadMessageW, WM_APP}; @@ -42,6 +42,10 @@ use windows::Win32::UI::WindowsAndMessaging::{PostThreadMessageW, WM_APP}; const IPC_SERVER_THREAD_NAME: &str = "wardoff-ipc-server"; const STATUS_SERVER_THREAD_NAME: &str = "wardoff-status-server"; const PIPE_BUFFER_SIZE: u32 = 4096; +// Includes the terminating newline; all current control commands are much smaller. +const MAX_REQUEST_BYTES: usize = 4096; +const REQUEST_READ_TIMEOUT: Duration = Duration::from_secs(1); +const REQUEST_READ_POLL_DELAY: Duration = Duration::from_millis(10); const PIPE_CONNECT_ATTEMPTS: usize = 20; const PIPE_CONNECT_DELAY: Duration = Duration::from_millis(100); const INITIAL_PIPE_CLAIM_ATTEMPTS: usize = 10; @@ -897,20 +901,95 @@ fn is_retryable_initial_pipe_claim_error(error_code: u32) -> bool { } fn read_request(pipe: &mut File) -> Result { - let mut request_line = String::new(); - let bytes_read = { - let mut reader = BufReader::new(pipe); - reader - .read_line(&mut request_line) - .map_err(|error| format!("Wardoff could not read an IPC request: {error}"))? - }; + // Nonblocking byte reads also handle a JSON line split across pipe messages. + // The pipe is owned by this server thread; no helper thread or pending I/O survives a timeout. + let handle = HANDLE(pipe.as_raw_handle()); + let read_mode = PIPE_READMODE_BYTE | PIPE_NOWAIT; + unsafe { SetNamedPipeHandleState(handle, Some(&read_mode), None, None) } + .map_err(|error| format!("Wardoff could not bound its IPC request read: {error}"))?; + + let result = read_request_until_deadline(pipe); + // Valid commands keep the existing reply behavior. Rejections remain nonblocking: + // a client that never reads its error reply must not stall the next accept either. + if result.is_ok() { + let reply_mode = PIPE_READMODE_MESSAGE | PIPE_WAIT; + unsafe { SetNamedPipeHandleState(handle, Some(&reply_mode), None, None) } + .map_err(|error| format!("Wardoff could not restore its IPC reply mode: {error}"))?; + } + result +} - if bytes_read == 0 { - return Err("Wardoff received an empty IPC request.".to_string()); +fn read_request_until_deadline(pipe: &mut File) -> Result { + let deadline = Instant::now() + REQUEST_READ_TIMEOUT; + let mut request_line = Vec::with_capacity(MAX_REQUEST_BYTES); + loop { + if Instant::now() >= deadline { + return Err("Wardoff timed out reading an IPC request.".to_string()); + } + + let mut buffer = [0; 512]; + let mut bytes_read = 0; + // std::fs::File::read maps ERROR_NO_DATA to EOF on Windows. Preserve that + // Win32 result here so an idle connected client really waits for the deadline. + let read = unsafe { + ReadFile( + HANDLE(pipe.as_raw_handle()), + Some(&mut buffer), + Some(&mut bytes_read), + None, + ) + }; + let error_code = unsafe { GetLastError() }; + match read { + Ok(()) if bytes_read == 0 => { + return Err(incomplete_request_error(request_line.is_empty())) + } + Ok(()) => { + let bytes_read = bytes_read as usize; + let newline = buffer[..bytes_read].iter().position(|byte| *byte == b'\n'); + let line_bytes = newline.map_or(bytes_read, |position| position + 1); + if request_line.len() + line_bytes > MAX_REQUEST_BYTES { + return Err(format!( + "Wardoff IPC request exceeds {MAX_REQUEST_BYTES} bytes." + )); + } + request_line.extend_from_slice(&buffer[..line_bytes]); + if newline.is_some() { + let line = std::str::from_utf8(&request_line) + .map_err(|error| format!("Wardoff received malformed IPC JSON: {error}"))?; + if line.trim().is_empty() { + return Err("Wardoff received an empty IPC request.".to_string()); + } + return serde_json::from_str(line.trim_end()) + .map_err(|error| format!("Wardoff received malformed IPC JSON: {error}")); + } + // A full buffer without a newline cannot complete within the frame limit. + if request_line.len() == MAX_REQUEST_BYTES { + return Err(format!( + "Wardoff IPC request exceeds {MAX_REQUEST_BYTES} bytes." + )); + } + } + Err(_) if error_code == ERROR_NO_DATA => { + // One absolute deadline, not a fresh timeout for every arriving byte. + thread::sleep( + REQUEST_READ_POLL_DELAY.min(deadline.saturating_duration_since(Instant::now())), + ); + } + Err(_) if error_code == ERROR_BROKEN_PIPE => { + return Err(incomplete_request_error(request_line.is_empty())); + } + Err(error) => return Err(format!("Wardoff could not read an IPC request: {error}")), + } } +} - serde_json::from_str(request_line.trim_end()) - .map_err(|error| format!("Wardoff received malformed IPC JSON: {error}")) +fn incomplete_request_error(empty: bool) -> String { + if empty { + "Wardoff received an empty IPC request.".to_string() + } else { + "Wardoff received an IPC request without a terminating newline.".to_string() + } } fn write_response(pipe: &mut File, response: &IpcResponse) -> Result<(), String> { diff --git a/tests/ipc_request_limits.rs b/tests/ipc_request_limits.rs new file mode 100644 index 0000000..9673166 --- /dev/null +++ b/tests/ipc_request_limits.rs @@ -0,0 +1,358 @@ +//! Runs the production IPC server on process-unique pipes with an inert UI consumer. +//! No blocker, autostart, singleton, logger, or production endpoint is started. +#![allow(dead_code)] + +use std::fs::{File, OpenOptions}; +use std::io::{Read, Write}; +use std::os::windows::io::AsRawHandle; +use std::sync::mpsc; +use std::sync::{ + atomic::{AtomicUsize, Ordering}, + Arc, +}; +use std::thread::{self, JoinHandle}; +use std::time::{Duration, Instant}; +use windows::Win32::Foundation::HANDLE; +use windows::Win32::System::Pipes::PeekNamedPipe; +use windows::Win32::System::Threading::GetCurrentThreadId; +use windows::Win32::UI::WindowsAndMessaging::{PeekMessageW, MSG, PM_NOREMOVE}; + +mod blocker { + pub(crate) enum BlockerMode { + Block, + Allow, + } +} + +mod cli { + #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] + pub(crate) struct StatusOutput { + pub(crate) state: String, + } + + impl StatusOutput { + pub(crate) fn to_json(&self) -> Result { + serde_json::to_string(self) + } + } +} + +mod logger { + pub(crate) enum EventSource { + Ipc, + } + + pub(crate) fn log_event(_: &str, _: EventSource, _: impl Into, _: bool) {} +} + +mod session_scope { + pub(crate) fn current_control_pipe_path() -> Result { + Ok(format!( + r"\\.\pipe\WardoffIpcTest-Control-{}", + std::process::id() + )) + } + + pub(crate) fn current_status_pipe_path() -> Result { + Ok(format!( + r"\\.\pipe\WardoffIpcTest-Status-{}", + std::process::id() + )) + } +} + +#[path = "../src/ipc.rs"] +mod ipc; + +struct TestRuntime { + server: ipc::IpcServer, + ui_thread: Option>, + dispatched: Arc, +} + +impl TestRuntime { + fn start() -> Self { + let (request_tx, request_rx) = mpsc::channel::(); + let (ready_tx, ready_rx) = mpsc::channel(); + let dispatched = Arc::new(AtomicUsize::new(0)); + let ui_dispatched = Arc::clone(&dispatched); + let ui_thread = thread::spawn(move || { + // PostThreadMessageW requires the target thread to own a message queue. + let mut message = MSG::default(); + unsafe { + let _ = PeekMessageW(&mut message, None, 0, 0, PM_NOREMOVE); + } + ready_tx + .send(unsafe { GetCurrentThreadId() }) + .expect("UI ready"); + for pending in request_rx { + ui_dispatched.fetch_add(1, Ordering::SeqCst); + let response = match pending.request { + ipc::IpcRequest::Status => ipc::IpcResponse::Status { + status: cli::StatusOutput { + state: "inert".into(), + }, + }, + _ => ipc::IpcResponse::Ok, + }; + let _ = pending.response_tx.send(response); + } + }); + let server = ipc::IpcServer::start(request_tx, ready_rx.recv().expect("UI thread ID")) + .expect("isolated IPC startup"); + Self { + server, + ui_thread: Some(ui_thread), + dispatched, + } + } + + fn stop(&mut self) { + self.server.shutdown().expect("clean IPC shutdown"); + if let Some(ui_thread) = self.ui_thread.take() { + ui_thread.join().expect("inert UI joined"); + } + } +} + +impl Drop for TestRuntime { + fn drop(&mut self) { + self.stop(); + } +} + +fn open_pipe(status: bool) -> File { + let path = if status { + session_scope::current_status_pipe_path() + } else { + session_scope::current_control_pipe_path() + } + .expect("isolated path"); + let deadline = Instant::now() + Duration::from_secs(3); + loop { + match OpenOptions::new().read(true).write(!status).open(&path) { + Ok(pipe) => return pipe, + Err(error) if Instant::now() >= deadline => panic!("connect {path}: {error}"), + Err(_) => thread::sleep(Duration::from_millis(10)), + } + } +} + +fn read_line_with_deadline(pipe: &mut File) -> Result { + let deadline = Instant::now() + Duration::from_secs(2); + let mut reply = Vec::new(); + loop { + let mut buffer = [0; 512]; + let mut available = 0; + // This test client is the sole reader, so observed bytes cannot be consumed elsewhere. + unsafe { + PeekNamedPipe( + HANDLE(pipe.as_raw_handle()), + None, + 0, + None, + Some(&mut available), + None, + ) + } + .map_err(|error| error.to_string())?; + let to_read = buffer.len().min(available as usize); + if to_read != 0 { + match pipe.read(&mut buffer[..to_read]) { + Ok(0) => return Err("server disconnected before replying".into()), + Ok(size) => { + reply.extend_from_slice(&buffer[..size]); + if reply.contains(&b'\n') { + return String::from_utf8(reply).map_err(|error| error.to_string()); + } + } + Err(error) => return Err(error.to_string()), + } + } + if Instant::now() >= deadline { + return Err("test client received no reply within 2 seconds".into()); + } + thread::sleep(Duration::from_millis(10)); + } +} + +fn exchange(payload: &[u8]) -> Result { + let mut pipe = open_pipe(false); + // Match send_request's separate JSON and newline pipe messages. + if let Some(json) = payload.strip_suffix(b"\n") { + pipe.write_all(json).map_err(|error| error.to_string())?; + pipe.write_all(b"\n").map_err(|error| error.to_string())?; + } else { + pipe.write_all(payload).map_err(|error| error.to_string())?; + } + serde_json::from_str(&read_line_with_deadline(&mut pipe)?).map_err(|error| error.to_string()) +} + +fn assert_normal_command() { + assert!(matches!( + exchange(b"{\"command\":\"set_mode\",\"mode\":\"allow\"}\n"), + Ok(ipc::IpcResponse::Ok) + )); +} + +fn assert_error(payload: &[u8], expected: &str) { + let reply = exchange(payload).expect("error response"); + assert!(matches!(reply, ipc::IpcResponse::Error { message } if message.contains(expected))); + assert_normal_command(); +} + +#[test] +fn isolated_runtime_rejects_bad_clients_and_recovers() { + let mut runtime = TestRuntime::start(); + assert_normal_command(); + assert!(matches!( + exchange(b"{\"command\":\"set_mode\",\"mode\":\"block\"}\r\n"), + Ok(ipc::IpcResponse::Ok) + )); + + // A rejected client may keep its handle open and refuse to read the error. + // The unknown variant diagnostic can be larger than the outbound pipe buffer. + let mut unread = open_pipe(false); + let command = "x".repeat(4070); + let payload = format!("{{\"command\":\"{command}\"}}\n"); + unread + .write_all(payload.as_bytes()) + .expect("large malformed write"); + assert_normal_command(); + drop(unread); + for (payload, expected) in [ + (&b"not JSON\n"[..], "malformed"), + (&b"\n"[..], "empty"), + (&b"{\"command\":\"unknown\"}\n"[..], "malformed"), + (&b"\xff\n"[..], "malformed"), + ] { + assert_error(payload, expected); + } + + // The cap counts wire bytes including LF, regardless of JSON whitespace or message size. + let mut boundary = b"{\"command\":\"status\"}".to_vec(); + boundary.resize(4095, b' '); + boundary.push(b'\n'); + assert!(matches!( + exchange(&boundary), + Ok(ipc::IpcResponse::Status { .. }) + )); + assert_normal_command(); + boundary.insert(boundary.len() - 1, b' '); + assert_error(&boundary, "exceeds 4096 bytes"); + assert_error(&vec![b' '; 4097], "exceeds 4096 bytes"); + + // Small individual messages must not bypass the cumulative frame cap. + let mut oversized = open_pipe(false); + for _ in 0..9 { + let _ = oversized.write_all(&[b' '; 512]); + } + let reply = read_line_with_deadline(&mut oversized).expect("fragmented oversized reply"); + drop(oversized); + assert!(reply.contains("exceeds 4096 bytes")); + assert_normal_command(); + + // EOF never turns an unterminated control command into an accepted request. + for payload in [ + &b""[..], + &b"{\"command\":\"set_mode\",\"mode\":\"allow\"}"[..], + ] { + let before = runtime.dispatched.load(Ordering::SeqCst); + let mut disconnected = open_pipe(false); + disconnected + .write_all(payload) + .expect("write before disconnect"); + drop(disconnected); + assert_normal_command(); + assert_eq!(runtime.dispatched.load(Ordering::SeqCst), before + 1); + } + + // A syntactically valid request without its newline must expire while open. + let started = Instant::now(); + let mut stalled = open_pipe(false); + stalled + .write_all(b"{\"command\":\"status\"}") + .expect("partial write"); + let status_started = Instant::now(); + let mut status_pipe = open_pipe(true); + let status = read_line_with_deadline(&mut status_pipe).expect("independent status response"); + let status_elapsed = status_started.elapsed(); + assert!( + status_elapsed < Duration::from_millis(500), + "status waited {status_elapsed:?}" + ); + eprintln!("status during incomplete request: {status_elapsed:?}"); + assert_eq!( + serde_json::from_str::(&status).expect("status JSON")["state"], + "inert" + ); + drop(status_pipe); + let reply = read_line_with_deadline(&mut stalled); + drop(stalled); // Unblocks the original reader too, even if this assertion fails. + assert!( + matches!(reply.as_ref().ok().and_then(|line| serde_json::from_str::(line).ok()), Some(ipc::IpcResponse::Error { message }) if message.contains("timed out")), + "{reply:?}" + ); + let partial_elapsed = started.elapsed(); + assert!(partial_elapsed >= Duration::from_millis(800)); + assert!(partial_elapsed < Duration::from_millis(1500)); + eprintln!("unterminated request rejected: {partial_elapsed:?}"); + assert_normal_command(); + + // A client sending nothing also expires; another queued client then succeeds. + let mut silent = open_pipe(false); + let normal = thread::spawn(assert_normal_command); + let reply = read_line_with_deadline(&mut silent).expect("silent timeout reply"); + drop(silent); + assert!(reply.contains("timed out")); + normal + .join() + .expect("normal client recovered after timeout"); + + // Trickle traffic must not reset the absolute deadline. + let mut slow = open_pipe(false); + let mut slow_writer = slow.try_clone().expect("slow writer handle"); + let started = Instant::now(); + let writer = thread::spawn(move || { + for _ in 0..20 { + if slow_writer.write_all(b" ").is_err() { + break; + } + thread::sleep(Duration::from_millis(80)); + } + }); + let reply = read_line_with_deadline(&mut slow); + drop(slow); + let elapsed = started.elapsed(); + writer.join().expect("slow writer joined"); + assert!(reply.expect("trickle timeout reply").contains("timed out")); + assert!(elapsed < Duration::from_millis(1500)); + assert!(elapsed >= Duration::from_millis(800)); + eprintln!("trickle request rejected: {elapsed:?}"); + assert_normal_command(); + + // Clean shutdown must still finish when the current control client is incomplete. + let mut shutdown_stall = open_pipe(false); + shutdown_stall + .write_all(b"{") + .expect("shutdown partial write"); + let (stopped_tx, stopped_rx) = mpsc::channel(); + let shutdown = thread::spawn(move || { + runtime.stop(); + stopped_tx.send(()).expect("shutdown result"); + }); + let stopped = stopped_rx.recv_timeout(Duration::from_secs(2)); + drop(shutdown_stall); + shutdown.join().expect("shutdown joined"); + stopped.expect("shutdown completed despite incomplete client"); + // Both owned endpoints are gone after orderly shutdown. + for path in [ + session_scope::current_control_pipe_path(), + session_scope::current_status_pipe_path(), + ] { + assert!(OpenOptions::new() + .read(true) + .open(path.expect("test path")) + .is_err()); + } +} From c3a674075a1f8dc789f55e0464f559200e75463d Mon Sep 17 00:00:00 2001 From: RomainROCH Date: Sun, 4 Oct 2026 20:17:28 +0200 Subject: [PATCH 06/15] chore: streamline agent and human development workflow --- .github/PULL_REQUEST_TEMPLATE.md | 13 +- .github/copilot-instructions.md | 157 +---------------- .gitignore | Bin 231 -> 109 bytes AGENTS.md | 67 ++++++++ CLAUDE.md | 44 +---- CONTRIBUTING.md | 177 +++++++++---------- PLAN.md | 13 +- README.md | 17 +- docs/ARCHITECTURE.md | 15 +- docs/BUSINESS_MODEL.md | 8 +- docs/DEVELOPMENT.md | 138 +++++++++++++++ docs/MVP_VALIDATION_MATRIX.md | 5 + docs/README.md | 43 +++++ scripts/check-docs.ps1 | 190 +++++++++++++++++++++ scripts/check.ps1 | 53 ++++++ src/autostart.rs | 4 +- src/config.rs | 1 - src/ipc.rs | 9 +- src/main.rs | 210 ++--------------------- src/runtime_policy.rs | 280 +++++++++++++++++++++++++++++++ tests/developer_checks.ps1 | 153 +++++++++++++++++ tests/smoke_test.ps1 | 9 + 22 files changed, 1092 insertions(+), 514 deletions(-) create mode 100644 AGENTS.md create mode 100644 docs/DEVELOPMENT.md create mode 100644 docs/README.md create mode 100644 scripts/check-docs.ps1 create mode 100644 scripts/check.ps1 delete mode 100644 src/config.rs create mode 100644 src/runtime_policy.rs create mode 100644 tests/developer_checks.ps1 diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index b0ff918..08f70a8 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -4,14 +4,13 @@ ## How it was tested -- [ ] `cargo check` passes -- [ ] `cargo test` passes -- [ ] `cargo build --release` passes -- [ ] `tests/smoke_test.ps1` passes (with expected admin-only skips) +- [ ] `scripts/check.ps1 -Mode Full` passes for Rust changes (or `-Mode Docs` for docs only) +- [ ] `tests/developer_checks.ps1` passes if developer tooling changed +- [ ] Relevant smoke/manual checks run in an isolated environment, or skips explained - [ ] Docs updated if behavior changed ## Checklist -- [ ] Branch created from `dev` -- [ ] No changes to `docs/ARCHITECTURE.md` without explicit approval -- [ ] No changes to `docs/BUSINESS_MODEL.md` without explicit approval +- [ ] Base branch stated; intended integration target follows CONTRIBUTING.md +- [ ] Intentional architecture changes include rationale; unrelated drift is reported +- [ ] Business-model changes reflect an explicit maintainer decision diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 3129fea..931ea7f 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -1,151 +1,10 @@ -# Copilot instructions +# Copilot entrypoint -This repository is an active Windows-only Rust application. `PLAN.md` remains the source of truth for product scope and MVP boundaries, but the repo now also contains a checked-in Cargo project, source tree, and a PowerShell smoke test script. +Read and follow [AGENTS.md](../AGENTS.md), the shared repository instruction +entrypoint, before editing. It links to canonical scope, architecture, +development workflow and validation documents. -## Read this first - -When you start work in this repo, read in this order: - -1. `README.md` for the current MVP snapshot -2. `PLAN.md` for current boundaries and prioritized next steps -3. `docs/ARCHITECTURE.md` for the code map -4. `docs/WINDOWS_SHUTDOWN_LAYERS.md` for detailed shutdown-layer behavior, especially Layer 2 ETW/local-shutdown nuance and limits -5. `CONTRIBUTING.md` for workflow and documentation expectations -6. this file for agent-specific guardrails - -Also read `docs/BUSINESS_MODEL.md` before changing any README, changelog, contributor guidance, support copy, or other repo messaging that touches monetization, signed binaries, sponsorship, consulting/support, or commercialization decisions. - -## Architecture authority guardrail - -- Treat `docs/ARCHITECTURE.md` as the canonical architecture source of truth. -- Do not "align `docs/ARCHITECTURE.md` to the code" just because implementation drift exists. -- Only update that file to record intentional architectural changes, implementation deviations plus rationale, impossibilities discovered during implementation, or current status. - -## Business-model authority guardrail - -- Treat `docs/BUSINESS_MODEL.md` as the canonical business-model source of truth. -- Do not invent or "normalize" sponsorship, funding, signed-binary, paid-support, consulting, or commercialization messaging from scattered repo hints. -- Read `docs/BUSINESS_MODEL.md` before changing README, changelog, contributor guidance, release messaging, funding/support copy, or any other documentation that could imply a business decision. - -## How to answer "what's next?" - -If a user asks only **"what's next?"**, answer from repo docs instead of inventing a roadmap: - -1. summarize the shipped MVP from `README.md` -2. state that the immediate priority is MVP hardening, validation, and documentation clarity from `PLAN.md` -3. mention later-phase backlog items only as backlog, not as shipped or currently committed work -4. stay conservative about local `shutdown.exe` handling and never promise that Wardoff stops `shutdown /t 0 /f` - -## Repository status and commands - -- The repo contains `Cargo.toml`, `src\`, and `tests\smoke_test.ps1`. -- Preferred build / validation commands: - - `cargo check` - - `cargo test` - - `cargo build --release` -- `cargo build --release` produces `target\release\wardoff.exe`, typically around ~1.5 MiB on this branch. -- `cargo check` and `cargo test` should pass on this branch. -- The dedicated smoke test entry point is `powershell -ExecutionPolicy Bypass -File .\tests\smoke_test.ps1`. -- Do not add CI workflows, release automation, or packaging work unless explicitly requested. - -## High-level architecture - -- `Wardoff` is a Windows-only Rust utility for protecting supported shutdown/reboot paths and preventing idle sleep and automatic display timeout. The intended target is `x86_64-pc-windows-msvc`. -- The product has two control surfaces that should stay aligned: - - a tray/background app with Block/Allow state and optional hidden mode - - a CLI exposing `--block`, `--allow`, `--status`, `--hide`, `--log`, `--tail`, `--autostart on|off`, and `--version`, with `--status` expected to produce JSON for scripting -- Keep the read-only CLI contract explicit: `--help`, `--version`, `--status`, and `--log --tail N` must remain non-elevating/local-or-read-only paths, while the default no-argument startup may still use the existing self-elevation path when needed. -- Keep the IPC split explicit when documenting CLI behavior: the session-scoped `WardoffControl` pipe is the state-changing path and the session-scoped `WardoffStatus` pipe is the dedicated read-only status path. -- Logging and observability are first-class: - - rotating JSON lines file logs - - shared counters and metadata such as total blocked attempts, last blocked attempt, and likely source -- Shutdown handling is intentionally layered: - 1. standard interactive shutdown blocking through a message-only window that handles `WM_QUERYENDSESSION`, calls `ShutdownBlockReasonCreate()`, and raises shutdown priority with `SetProcessShutdownParameters()` - 2. local `shutdown.exe` protection is not part of the current documented MVP surface - 3. Windows Update reboot protection by disabling the scheduled task `Microsoft\Windows\UpdateOrchestrator\Reboot` and re-checking it periodically when that task exists - 4. remote shutdown protection via repeated `AbortSystemShutdown(NULL)` polling -- Be explicit that Layer 3 can skip cleanly on editions such as LTSC where `\Microsoft\Windows\UpdateOrchestrator\Reboot` is absent; that is normal behavior, not a failure. -- For detailed layer-by-layer behavior and the cautious wording around ETW/local shutdown handling, read `docs/WINDOWS_SHUTDOWN_LAYERS.md`. -- Idle-sleep/display-timeout prevention follows Block/Allow through process-owned Power Requests. Follow `docs/ARCHITECTURE.md` and `docs/MVP_VALIDATION_MATRIX.md` for lifecycle, resume behavior and Windows limits; do not promise an explicit Sleep/Hibernate veto. -- Autostart is handled via Task Scheduler rather than the `Run` registry key so elevated scenarios can be handled correctly. - -## Implemented module structure - -- `src/main.rs`: CLI dispatch, bootstrap, message loop, tray coordination, shutdown cleanup -- `src/cli.rs`: clap definitions, requested-action mapping, JSON status payloads -- `src/blocker/mod.rs`: blocker coordinator and shared power-action helpers -- `src/blocker/shutdown.rs`: Layer 1 shutdown/sign-out handling and shutdown block reason ownership -- `src/blocker/local.rs`: current local shutdown worker implementation; keep its user-facing documentation conservative and aligned with MVP scope decisions -- `src/blocker/update.rs`: UpdateOrchestrator task monitoring and restore logic -- `src/blocker/remote.rs`: Layer 4 `AbortSystemShutdownW(None)` polling loop -- `src/blocker/sleep.rs`: owned system/display Power Requests -- `src/blocker/abort.rs`: shared shutdown-abort privilege and result helpers -- `src/autostart.rs`: scheduled-task autostart management -- `src/instance.rs` and `src/ipc.rs`: single-instance ownership plus the named-pipe control and read-only status paths -- `src/logger\`: human logging plus rotating structured JSONL logging -- `src/tray\`: tray icon/menu surface and tray action plumbing -- `src/windows_util.rs`: elevation checks and relaunch helpers -- `src/config.rs`: app data and path helpers - -## Project-specific conventions from `PLAN.md` - -- Re-read `PLAN.md` before making major structural decisions. Use it as the roadmap and scope boundary document, while `README.md` stays the quickest current-state summary. -- Preserve the distinction between MVP and later phases: - - MVP is the safe, official-API release: standard shutdown blocking, UpdateOrchestrator handling, remote abort loop, tray basics, CLI basics, Task Scheduler autostart, idle-sleep/display-timeout prevention, and simple file logging - - aggressive IFEO mode, Windows Event Log, profiles, timer, and toast notifications belong to later phases unless the plan is updated -- Be explicit about elevation boundaries. Update protection and aggressive `shutdown.exe` interception are planned as admin-only features; tray, UI, and CLI flows should surface that requirement clearly rather than failing silently. -- Prefer official Windows APIs first. Aggressive behavior is opt-in and should carry strong warnings because it may trigger EDR tooling. -- The plan explicitly targets `Microsoft\Windows\UpdateOrchestrator\Reboot`, not the older `MusNotification` approach. -- Do not hide platform limits. The plan is explicit that `shutdown /t 0 /f` cannot be blocked from user space; future code and docs should log that case honestly instead of claiming success. -- Keep the project scriptable and sysadmin-friendly: - - machine-readable CLI output for status - - structured JSONL log data that can be queried from PowerShell or other tooling - - Task Scheduler usage over ad-hoc startup hooks -- Keep README/architecture/copilot guidance aligned with the documented exit-code contract in README rather than inventing new CLI result semantics. -- UI semantics that matter in the current MVP: Block state is visually red and Allow is green. - -## Scope guardrails - -These rules apply to EVERY change in this repo. Re-read them before starting any task. - -### What is IN scope (MVP) -Only these features belong in the current phase: -- Layer 1: ShutdownBlockReasonCreate + WM_QUERYENDSESSION + SetProcessShutdownParameters -- Layer 3: Task Scheduler UpdateOrchestrator\Reboot disable/re-check -- Layer 4: AbortSystemShutdown polling loop -- Idle-sleep/display-timeout prevention via Power Requests; no screensaver, lock or explicit Sleep/Hibernate veto -- Tray icon: Block/Allow toggle, right-click menu (Block, Allow, Shutdown, Reboot, Sleep, Hibernate, Quit) -- CLI: --block, --allow, --status (JSON output), --hide, --log, --tail, --autostart on|off, --version -- Auto-start via Task Scheduler -- File logging: rotating JSON lines (timestamp, event type, source, action, success/failure) -- README.md with technical documentation - -### What is OUT of scope (v1.0 or later — do NOT implement) -- IFEO aggressive mode -- Windows Event Log integration (EventLog provider) -- Toast notifications -- Timer functionality -- Profiles (Gaming, Work, Update Shield, Custom) -- Settings window / GUI beyond tray menu -- Winget/Scoop/Chocolatey packaging -- CI/CD pipelines, GitHub Actions workflows -- Release workflows, signing, packaging scripts -- Benchmarks, performance testing infrastructure - -### Behavior rules for every commit -1. **No unrequested work.** Do not add features, files, configs, or infrastructure not explicitly asked for. No CI configs. No release workflows. No architectural refactoring beyond the current task. -2. **PLAN.md is the source of truth.** If a task contradicts PLAN.md, stop and ask. Do not silently deviate. -3. **One branch per fix/feature when a task actually requires repo changes.** If the task is explicitly read-only or branchless, follow that instruction instead of creating a branch. Otherwise create a focused branch such as `feat/description` or `fix/description`, implement the change, then provide instructions to merge into the dev branch. -4. **No scope creep into v1.0 features.** If a task seems to require a v1.0 feature, flag it explicitly: "This requires [feature X] which is marked as v1.0 in PLAN.md. Should I proceed?" -5. **Document limitations honestly.** If something cannot be done (e.g., blocking `shutdown /t 0 /f`), document it in comments and README instead of implementing a hacky workaround. -6. **Admin boundaries are explicit.** Features requiring elevation must check for admin rights and fail with a clear message, not silently degrade. -7. **Document user-facing status conservatively.** Do not describe IFEO, ETW local shutdown interception, Windows Event Log, toasts, timers, profiles, or settings as shipped user-facing features unless the plan is updated and the task explicitly asks for that. -8. **Test what you build.** After implementing a feature, compile it (`cargo build --release`) and verify it runs. If it requires Windows APIs that can only be tested at runtime, document what to test manually. - -### Code conventions -- All public items must have `///` doc comments -- Use `log` crate macros (info!, warn!, error!) for all operational messages -- No `unwrap()` or `expect()` in production code paths — use proper error handling -- Module structure must match the layout defined in this file (see Implemented module structure above) -- Minimum Rust edition: 2021 -- Target: x86_64-pc-windows-msvc only +Use the [task-to-code map](../docs/DEVELOPMENT.md#task-to-code-map) to load only +relevant modules. Keep project guidance in those shared owners rather than +duplicating it here. Preserve the Windows-only product and its human-facing +tray and CLI behavior. diff --git a/.gitignore b/.gitignore index 3e1beb30af95532f5fc66efa777ad251f8c0301d..f2416207cd932d673c021ab8411f94ae388f8d1f 100644 GIT binary patch delta 88 zcmaFPnCqrrl30|UTEeBJSCEnfB#VmmlCrs6BV3(BLp=Tc;)6pRLqdb~N-9dY3X1Y` n3rh5IQ@C7Q!{Ys1BSL_(t^vV7k%>OCTwHp|IfggN3 literal 231 zcmbu0u?oW=6h-eW_#+)&zQGy?!5AT0vW29E4y{yD3;q0>%-uR&IOp7^%-d}(oSi7g zWuNUu%bhbdDjB@)?KF7d!$9RztGqo^E|el?t-4HBjlO#2h6A~9Hd;=y@48^M6vy2e gVGm2V;(-}&M677Q!60EE5YR$_kgD0~f90?D7yY;}I{*Lx diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..863d5ac --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,67 @@ +# Working on Wardoff + +Wardoff is a Windows-only Rust application for shutdown protection and idle-power +requests. Keep the tray and CLI usable by people; agent guidance belongs in the +development docs, not in the product interface. + +## Find the owner before editing + +Read [CONTRIBUTING.md](CONTRIBUTING.md) for the workflow, then use the +[task-to-code map](docs/DEVELOPMENT.md#task-to-code-map) to open only the relevant +modules and tests. [docs/README.md](docs/README.md) indexes the rest. + +| Decision | Canonical source | +| --- | --- | +| Product scope and next work | [PLAN.md](PLAN.md) | +| Intended architecture and intentional deviations | [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) | +| Supported commands and user-visible limits | [README.md](README.md) | +| Validation and Windows-dependent acceptance | [docs/MVP_VALIDATION_MATRIX.md](docs/MVP_VALIDATION_MATRIX.md) | +| Business, sponsorship, signing and paid support | [docs/BUSINESS_MODEL.md](docs/BUSINESS_MODEL.md) | + +Report material conflicts. Do not silently redefine architecture to match code +drift or promote roadmap items into shipped features. Read the business-model +document when the task touches that subject. Dated audits, benchmarks and drafts +are evidence or working copy, not a replacement for these owners. + +## Build and validate + +Use Windows 10/11, Rust stable with the MSVC x64 toolchain, Git and PowerShell +5.1 or newer. Run from the repository root: + +```powershell +powershell -NoProfile -ExecutionPolicy Bypass -File scripts/check.ps1 +powershell -NoProfile -ExecutionPolicy Bypass -File scripts/check.ps1 -Mode Full +powershell -NoProfile -ExecutionPolicy Bypass -File scripts/check.ps1 -Mode Docs +``` + +Default `Fast` checks documentation, formatting, compilation and unit tests. +`Full` also runs strict Clippy and builds the release binary. `Docs` only checks +local documentation links and encoding. The scripts stop on failure and never +launch Wardoff. Details and focused tests live in +[DEVELOPMENT.md](docs/DEVELOPMENT.md#validation). + +`tests/smoke_test.ps1` is a separate, state-changing integration suite: it stops +repo-built Wardoff processes, runs Block/Allow and can change scheduled tasks. +Use an isolated Windows test environment; see the validation matrix. A successful +build or unit test is not evidence of actual shutdown, suspend or tray behavior. + +## Preserve the important boundaries + +- `src/main.rs` owns effects and the application loop; + `src/runtime_policy.rs` owns pure startup, command and tray decisions. +- Read-only CLI actions must stay non-elevating. Keep control and status pipes + separate and session-scoped; preserve same-user access checks. +- Preserve layer activation rollback, handle ownership, cleanup and wake restore. +- Keep Layer 2 ETW wording conservative. Do not promise forced zero-second + shutdown blocking or an explicit Sleep/Hibernate veto. +- Follow [CONTRIBUTING.md](CONTRIBUTING.md) for conventions and branch workflow. + Do not add dependencies, CI/release infrastructure or later-phase product + features as incidental cleanup. + +## Finish a change + +Run focused checks while iterating and `Full` for Rust changes. For docs-only +changes use `Docs`; for developer-tooling changes also run +`tests/developer_checks.ps1`. Record failures and skipped Windows/manual checks. +Update the owning doc when its contract changes and keep links working. Do not +duplicate this guide into harness-specific files. diff --git a/CLAUDE.md b/CLAUDE.md index 403e931..6c2e6dd 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,42 +1,6 @@ -# --- CLAUDE.md pour Wardoff (racine du repo) --- +@AGENTS.md -# Projet +# Claude entrypoint -Application Rust Windows : bloqueur de shutdown en 4 couches. -MVP tagué `v0.1.0-mvp`. Repo public, licence MIT. - -# Build et vérification - -``` -cargo build --release -cargo test -cargo clippy -- -D warnings -``` - -Exécute les trois après chaque changement. Binary cible < 2MB, zéro runtime deps. - -# Architecture - -4 couches de blocage shutdown — consulter `src/` directement. -Architecture détaillée : @docs/ARCHITECTURE.md -Business model : @docs/BUSINESS_MODEL.md - -# Tests - -`cargo test` couvre les tests unitaires et tourne sans privilèges. -Les couches 3 (Update Orchestrator) et 4 (remote shutdown) ne s'arment qu'en session élevée — leur validation est manuelle. -Procédure complète et matrice : @docs/MVP_VALIDATION_MATRIX.md - -# Git - -- Branche principale : `main` -- Tags sémantiques : `v0.x.y` -- Commits atomiques -- Format Conventional Commits : `type: sujet` en minuscules. Types utilisés : `feat`, `fix`, `docs`, `chore`, `refactor`, `test`, `merge`. Sujet à l'impératif présent, sans point final. - -# Ne PAS faire - -- Pas de portabilité cross-platform — Windows only, c'est intentionnel -- Pas de telemetry, pas de paywall, pas de tracking -- Ne pas ajouter de dépendances sans demander -- Ne pas utiliser `unwrap()` en production +The shared repository instructions are in [AGENTS.md](AGENTS.md). +Use its task routing and validation commands; maintain project guidance there. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bc7f88b..c070f1c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,114 +1,97 @@ # Contributing to Wardoff -Thanks for contributing to Wardoff. Please also review the -[Code of Conduct](CODE_OF_CONDUCT.md). +Start with the [README](README.md) for the product and the +[development guide](docs/DEVELOPMENT.md) to find code and tests for your task. +Please follow the [Code of Conduct](CODE_OF_CONDUCT.md). +Agents start at [AGENTS.md](AGENTS.md); humans and agents use the same workflow. -This repository now contains a working Windows MVP runtime, but the docs should remain conservative: describe what is clearly implemented and supported, and do not turn later-phase ideas into shipped features. +## Set up and check the project -## Read this first - -If you are new to the repo, read these before making changes: - -1. `README.md` for the current MVP summary -2. `PLAN.md` for scope boundaries and the current "what's next" answer -3. `docs/ARCHITECTURE.md` for the code map - -## Business-model messaging guardrail - -If your change touches sponsor, funding, signed-binary, paid-support, consulting, or commercialization messaging, read `docs/BUSINESS_MODEL.md` first and keep the wording aligned with it. Do not invent offers or promises ad hoc. - -## Prerequisites - -For local development on Windows, use: - -- Windows 10 or Windows 11 -- Rust stable -- MSVC Build Tools for the `x86_64-pc-windows-msvc` target -- Git - -Administrator rights are required for some manual validation paths and some runtime features, especially Update Orchestrator task handling and autostart task changes. - -## Build - -The main build command to use and document is: +Use Windows 10 or 11, Git, PowerShell 5.1 or newer, and Rust stable targeting +`x86_64-pc-windows-msvc`. Install MSVC C++ build tools and a Windows SDK. +The normal development checks do not require administrator rights. ```powershell -cargo build --release +powershell -NoProfile -ExecutionPolicy Bypass -File scripts/check.ps1 ``` -Useful supporting commands: +For Rust changes, finish with the full check, including strict Clippy and the +release build: ```powershell -cargo fmt --check -cargo clippy --all-targets --all-features -cargo test +powershell -NoProfile -ExecutionPolicy Bypass -File scripts/check.ps1 -Mode Full ``` -## Smoke test +The [development guide](docs/DEVELOPMENT.md#validation) lists individual commands, +focused test filters, documentation checks and troubleshooting. -Run the repo smoke test with: +## Runtime validation is separate + +The [validation matrix](docs/MVP_VALIDATION_MATRIX.md) owns runtime acceptance. +The smoke suite stops repo-built Wardoff processes, starts Block/Allow runtimes, +appends logs, and can change Task Scheduler entries. Run it in an isolated +Windows test environment where those effects are acceptable: ```powershell -powershell -ExecutionPolicy Bypass -File tests\smoke_test.ps1 +powershell -NoProfile -ExecutionPolicy Bypass -File tests/smoke_test.ps1 ``` -For the current MVP validation split between automated coverage, manual checks, admin-only checks, and VM-only checks, see [`docs/MVP_VALIDATION_MATRIX.md`](docs/MVP_VALIDATION_MATRIX.md). - -Use a disposable VM for disruptive shutdown, reboot, sleep, hibernate, or Task Scheduler validation. - -## Current implementation boundaries - -Keep documentation and PR descriptions aligned with the current supported MVP surface: - -- interactive shutdown/sign-out blocking is implemented -- Update Orchestrator reboot-task protection is implemented -- remote shutdown abort polling is implemented -- idle-sleep and automatic display-timeout prevention are implemented; explicit Sleep/Hibernate remain controlled by Windows -- tray, CLI, logging, IPC, autostart, and single-instance coordination are implemented - -Do **not** claim the following as implemented unless your change really adds and validates them: - -- IFEO interception -- Windows Event Log integration -- toast notifications -- timers -- profiles -- settings UI - -Be especially careful with local `shutdown.exe` wording: - -- do not promise that Wardoff blocks `shutdown /t 0 /f` -- do not turn experimental or cautiously documented behavior into a marketing claim - -## Branch workflow - -Use this workflow unless a maintainer tells you otherwise: - -1. branch from `dev` -2. use a branch name such as `feat/xxx` or `fix/xxx` -3. merge completed work back into `dev` -4. open the PR to `main` from the appropriate integrated branch state - -Keep changes focused and reviewable. - -## Pull request checklist - -In each PR: - -- explain what changed -- explain what you tested -- call out any admin requirement -- call out any behavior that is intentionally still planned rather than shipped -- keep docs and code wording consistent - -## Coding and documentation conventions - -- keep documentation in English -- prefer `cargo build --release` when describing the real build process -- use fenced code blocks with language tags -- use `rustfmt` and `clippy` -- avoid `unwrap()` and `expect()` in production paths where failure should be surfaced cleanly -- add `///` comments to public Rust items where appropriate -- keep claims honest and source-verifiable - -Small, accurate PRs are much easier to review than broad speculative rewrites. +Administrator rights enable additional coverage. A missing UpdateOrchestrator +Reboot task is an expected skip on some Windows editions. Use a disposable VM +for shutdown/reboot/sign-out and suitable test hardware or a capable VM for +physical sleep/hibernate. Record skips and untested behavior rather than +equating unit tests with end-to-end acceptance. + +## Scope and authority + +- [PLAN.md](PLAN.md) owns product scope and priorities. +- [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) owns intended architecture. Record + intentional changes with their reason; report drift instead of silently + rewriting the design to fit it. +- [docs/BUSINESS_MODEL.md](docs/BUSINESS_MODEL.md) owns monetization, sponsorship, + signed binaries and paid support. Read it before changing related messaging. +- [docs/WINDOWS_SHUTDOWN_LAYERS.md](docs/WINDOWS_SHUTDOWN_LAYERS.md) explains layer + limits. Do not promise to stop `shutdown /t 0 /f` or explicit sleep/hibernate. + +Keep IFEO, Event Log, toasts, timers, profiles, settings and packaging as backlog +unless the task explicitly changes that scope. Existing CI/release workflows +are infrastructure to maintain when requested, not evidence of a new product +feature. Do not add CI, release automation or benchmarks as incidental cleanup. + +## Branches and commits + +Unless the task specifies a starting point, branch from `dev`, use a focused +feature/fix branch (agents may use `codex/`), and target `dev` for +integration. Releases reach `main` from the integrated state. Preserve existing +work when continuing from another branch and state which base you used. + +Use atomic Conventional Commits: `type: imperative subject`, lowercase with no +trailing period. Explain the change and validation; reference an issue only +when one exists. A local change does not by itself authorize publication. + +## Code and documentation conventions + +- Keep documentation, code comments and user-visible copy in English. +- Use Rust 2021, `rustfmt` and Clippy. Keep the Windows x64 MSVC target. +- Handle recoverable production errors rather than using `unwrap()` or + `expect()`; test assertions and fail-fast build-script diagnostics differ. +- Document public Rust items and non-obvious Win32 ownership/thread constraints. +- Use `log` macros for operational messages and the existing structured logger + for events; preserve machine-readable CLI output and exit codes. +- Preserve red Block / green Allow tray semantics, the non-elevating read-only + CLI, session-scoped IPC, admin boundaries and transactional cleanup. +- Keep pure decisions separate from Windows effects when that makes the change + easier to test. Avoid framework layers or module splits without a concrete need. +- Discuss new project dependencies with the maintainer. Keep protection free + of telemetry, tracking and paywalls. +- Link to an existing documentation owner instead of duplicating its rules. + Use relative Markdown links and fenced code blocks with language tags. +- Keep dated evidence and unpublished drafts clearly labeled. Do not turn + measurements from one machine into a general guarantee. + +## Review checklist + +Explain delivered behavior, affected modules, tests run and remaining +manual/admin checks. Update relevant docs when a contract changes. Report +architecture deviations, security implications and product limits explicitly. +Use the [pull request template](.github/PULL_REQUEST_TEMPLATE.md) for a PR. diff --git a/PLAN.md b/PLAN.md index 4372e0c..11051c0 100644 --- a/PLAN.md +++ b/PLAN.md @@ -2,18 +2,17 @@ This file is the repo-level status and roadmap summary for Wardoff. -If you are new here, read in this order: - -1. `README.md` for the current user-facing MVP snapshot -2. `PLAN.md` for current status, boundaries, and likely next work -3. `docs/ARCHITECTURE.md` for the code map -4. `CONTRIBUTING.md` and `.github/copilot-instructions.md` for workflow and scope guardrails +For the product, start with [README.md](README.md). For development, use +[CONTRIBUTING.md](CONTRIBUTING.md) and the +[task-to-code map](docs/DEVELOPMENT.md#task-to-code-map). Agents start at +[AGENTS.md](AGENTS.md). This plan owns scope and priorities, not setup commands. For monetization, sponsorship, signed-binary, paid-support, or broader commercialization decisions, follow `docs/BUSINESS_MODEL.md` rather than inferring policy from engineering docs. ## Current repository status -Wardoff is currently a shipped, source-first **0.1.0-style MVP** for Windows. +Wardoff is a source-first Windows MVP. [Cargo.toml](Cargo.toml) owns the current +package version; the scope below describes supported behavior, not release status. The branch already contains a working runtime plus CLI centered on one main promise: keep a machine in a visible **Block** state using conservative, documented Windows techniques where possible. diff --git a/README.md b/README.md index 885753d..2ff5e78 100644 --- a/README.md +++ b/README.md @@ -47,16 +47,13 @@ Wardoff currently ships a working tray/runtime app plus CLI with these MVP-level ## Documentation entrypoints -If you are brand-new to the repo, read these in order: - -1. **`README.md`** - current user-facing MVP snapshot -2. **[`PLAN.md`](PLAN.md)** - current status, boundaries, and prioritized next steps -3. **[`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md)** - where the runtime pieces live in code -4. **[`docs/BUSINESS_MODEL.md`](docs/BUSINESS_MODEL.md)** - source of truth for monetization, signed binaries, sponsorship, consulting/support, and commercialization decisions -5. **[`docs/WINDOWS_SHUTDOWN_LAYERS.md`](docs/WINDOWS_SHUTDOWN_LAYERS.md)** - detailed shutdown-layer behavior, including Layer 2 ETW/local-shutdown nuances and limits -6. **[`docs/MVP_VALIDATION_MATRIX.md`](docs/MVP_VALIDATION_MATRIX.md)** - acceptance and validation guide covering smoke coverage, tray and Explorer-restart checks, handoff-focused spot checks, and admin-only validation -7. **[`CONTRIBUTING.md`](CONTRIBUTING.md)** - workflow, testing, and documentation guardrails -8. **[`.github/copilot-instructions.md`](.github/copilot-instructions.md)** - agent-specific read order and scope rules +- **Using Wardoff:** continue with installation and usage below. +- **Developing Wardoff:** start with [CONTRIBUTING.md](CONTRIBUTING.md), then the + [development guide](docs/DEVELOPMENT.md) for task-to-code routing and checks. +- **Working as an agent:** start with [AGENTS.md](AGENTS.md); it uses the same + workflow and documentation as human contributors. +- **Finding a specific topic:** use the [documentation index](docs/README.md). +- **Choosing the next product change:** read [PLAN.md](PLAN.md). ## Not implemented yet diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 282fe05..128db58 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -42,6 +42,16 @@ Owns application bootstrap and shutdown: This is the best entry point for understanding the whole runtime. +### `src/runtime_policy.rs` (intentional responsibility extraction) + +Pure decisions formerly embedded in `main.rs` live together with their unit tests: +startup mode and tray surface, secondary CLI-to-IPC mapping, pending wake restore, +and the autostart checkbox fallback. This makes those rules discoverable and +testable without navigating Win32 orchestration. The runtime behavior, protocol, +thread ownership and effectful lifecycle remain unchanged; `main.rs` still applies +the decisions. The empty `config.rs` placeholder was removed because it owned no +configuration behavior. + ### `src/blocker/` Contains the layered protection logic. @@ -86,6 +96,7 @@ Contains Task Scheduler integration for Start with Windows behavior. - `src/cli.rs` — argument parsing and status output - `src/windows_util.rs` — elevation checks, relaunch, and Windows-specific helpers +- `src/session_scope.rs` — shared Windows session ID and mutex/pipe names - `build.rs` + `wardoff.manifest` — release-manifest embedding and requested execution level ## Core runtime object: `BlockerCoordinator` @@ -431,7 +442,9 @@ That code exists and is wired in, but current top-level user-facing MVP docs del ## Suggested reading order for new developers -If you are new to the codebase, read in this order: +For a focused change, start with the +[task-to-code map](DEVELOPMENT.md#task-to-code-map). For a full runtime walkthrough, +read in this order: 1. `src/main.rs` 2. `src/blocker/mod.rs` diff --git a/docs/BUSINESS_MODEL.md b/docs/BUSINESS_MODEL.md index 27f8c5f..61b27ef 100644 --- a/docs/BUSINESS_MODEL.md +++ b/docs/BUSINESS_MODEL.md @@ -149,7 +149,7 @@ This section tracks when to reassess or add new monetization elements. ## What this document does NOT cover -- Technical architecture (see `ARCHITECTURE.md`) -- Product roadmap and feature scope (see `../PLAN.md`) -- Community outreach strategy (see `EARLY_ADOPTER_OUTREACH_PLAN.md`) -- Manual validation (see `MVP_VALIDATION_MATRIX.md` and `MANUAL_VALIDATION_PLAN.md`) +- Technical architecture (see [Architecture](ARCHITECTURE.md)) +- Product roadmap and feature scope (see [Plan](../PLAN.md)) +- Community outreach copy (see the unpublished [Outreach drafts](OUTREACH_DRAFTS.md)) +- Manual validation (see [MVP validation matrix](MVP_VALIDATION_MATRIX.md)) diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md new file mode 100644 index 0000000..c401f34 --- /dev/null +++ b/docs/DEVELOPMENT.md @@ -0,0 +1,138 @@ +# Development guide + +This guide helps humans and agents make a focused change without reading the +whole repository. [CONTRIBUTING.md](../CONTRIBUTING.md) owns workflow and coding +conventions; [ARCHITECTURE.md](ARCHITECTURE.md) owns the design. + +## First working checkout + +Use Windows 10/11 with Rust stable, the `x86_64-pc-windows-msvc` toolchain, MSVC +C++ build tools, a Windows SDK, Git and PowerShell 5.1+. From the repository root: + +```powershell +rustc -Vv +cargo -V +powershell -NoProfile -ExecutionPolicy Bypass -File scripts/check.ps1 +``` + +The first Cargo invocation downloads locked dependencies. Keep `Cargo.lock` +checked in; routine validation uses `--locked` so it cannot silently resolve a +different dependency set. Cargo commands use the configured target directory. +The default release output is `target/release/wardoff.exe`; an explicit +`CARGO_BUILD_TARGET` adds a target-triple subdirectory. + +Building does not launch Wardoff. `cargo run` does: default startup can ask for +elevation and arm protection. For runtime testing, first read the +[validation matrix](MVP_VALIDATION_MATRIX.md). + +## Task-to-code map + +Open the owner and its tests first. Expand to the companion when the change +crosses that boundary. Unit tests live in `#[cfg(test)]` modules beside the code. + +| Task | First code | Companion / acceptance | +| --- | --- | --- | +| CLI arguments, conflicts, JSON status | [cli.rs](../src/cli.rs) | [main.rs](../src/main.rs) dispatch; README command contract | +| Startup mode, tray visibility, command mapping | [runtime_policy.rs](../src/runtime_policy.rs) | `run_main`, `handle_primary_runtime_request` in main | +| Elevation, console detach, relaunch | [windows_util.rs](../src/windows_util.rs) | `prepare_default_launch`, `prepare_runtime_console_launch` in main; smoke manifest/CLI checks | +| Mode transitions, activation rollback | [blocker/mod.rs](../src/blocker/mod.rs) | `Application::set_mode` in main; affected layer | +| Interactive shutdown and Windows callbacks | [blocker/shutdown.rs](../src/blocker/shutdown.rs) | main callbacks; VM-only shutdown acceptance | +| Idle-power requests, cleanup and resume | [blocker/sleep.rs](../src/blocker/sleep.rs) | runtime policy wake tests; main `handle_power_broadcast`; physical resume checks | +| Update reboot task | [blocker/update.rs](../src/blocker/update.rs) | validation matrix admin/absent-task cases | +| Remote abort / local ETW | [blocker/remote.rs](../src/blocker/remote.rs), [blocker/local.rs](../src/blocker/local.rs) | [abort.rs](../src/blocker/abort.rs); shutdown-layer limits | +| Control/status transport, retries, access checks | [ipc.rs](../src/ipc.rs) | main `handle_ipc_request`; security audit residual risks | +| Session names and singleton handoff | [session_scope.rs](../src/session_scope.rs), [instance.rs](../src/instance.rs) | main `forward_request_to_primary`; handoff acceptance | +| Tray menu, Explorer restart, icon | [tray/mod.rs](../src/tray/mod.rs), [tray/icon.rs](../src/tray/icon.rs) | main `handle_tray_action`; manual tray checks | +| Autostart / trusted executable path | [autostart.rs](../src/autostart.rs) | runtime policy checkbox fallback; elevated smoke checks | +| JSONL events, rotation and tail | [logger/mod.rs](../src/logger/mod.rs) | main read-only log dispatch; smoke log checks | +| Release manifest | [build.rs](../build.rs), [wardoff.manifest](../wardoff.manifest) | release build and smoke manifest check | +| Developer checks and docs links | [check.ps1](../scripts/check.ps1), [check-docs.ps1](../scripts/check-docs.ps1) | [developer_checks.ps1](../tests/developer_checks.ps1) | + +## Runtime flow and invariants + +`cli::parse_cli` determines intent. `main::run_main` handles read-only status/log +before runtime bootstrap. Runtime commands claim the session mutex: a secondary +forwards to IPC; a primary prepares elevation/console behavior and calls +`bootstrap`, then `run`. + +The main application thread owns mode changes and cleanup. Pipe workers and the +tray thread send work back to it. Preserve this ownership when changing callbacks: +the global application pointer is only valid while `run` owns the application +and the callbacks are registered. + +`runtime_policy` contains decisions without Windows effects: initial mode/tray +surface, secondary command mapping, wake restoration and autostart checkbox +fallback. It must not acquire handles, spawn threads, access tasks or log events. +Effects remain in the existing runtime and subsystem owners. + +The two pipes have different contracts: status reads must remain available +without elevation; control commands retain same-user/session access checks. +The inactive status path retries both pipes and may take about four seconds. +That known limitation is tracked in [PLAN.md](../PLAN.md). + +## Validation + +| Scope | Command | What it establishes | +| --- | --- | --- | +| Normal iteration | `powershell -NoProfile -ExecutionPolicy Bypass -File scripts/check.ps1` | Docs, formatting, compilation and unit tests | +| Rust change ready for review | `powershell -NoProfile -ExecutionPolicy Bypass -File scripts/check.ps1 -Mode Full` | Fast checks, strict Clippy, release build | +| Documentation only | `powershell -NoProfile -ExecutionPolicy Bypass -File scripts/check.ps1 -Mode Docs` | Local Markdown destinations exist; valid UTF-8 without NUL bytes | +| Developer scripts | `powershell -NoProfile -ExecutionPolicy Bypass -File tests/developer_checks.ps1` | Checker success/failure paths in temporary fixtures | +| Runtime integration | `powershell -NoProfile -ExecutionPolicy Bypass -File tests/smoke_test.ps1` | State-changing Windows integration; isolated environment | + +The check script resolves the repository from its own location, stops at the +first failed command and returns a nonzero exit code. It does not install tools, +launch the product or run the smoke suite. Unit tests include brief real Windows +Power Request acquisition/release and token checks; they do not launch the +long-lived runtime, edit scheduled tasks or cause a physical power transition. + +Individual Cargo commands, useful for focused diagnosis: + +```powershell +cargo fmt --all -- --check +cargo check --locked +cargo test --locked +cargo clippy --locked --all-targets --all-features -- -D warnings +cargo build --release --locked +``` + +Examples of focused tests: + +```powershell +cargo test --locked cli::tests +cargo test --locked runtime_policy::tests +cargo test --locked blocker::sleep::tests +cargo test --locked ipc::tests +cargo test --locked instance::tests +cargo test --locked session_scope::tests +cargo test --locked autostart::tests +``` + +The docs checker covers tracked and untracked, non-ignored Markdown. It checks +local link destinations, not heading fragments, external websites or factual +accuracy. Review those explicitly when changing them. Dated benchmark scripts +remain separate from development checks. + +Existing [CI](../.github/workflows/ci.yml) runs Cargo check/test, a release build +and smoke tests on Windows. The local Full check adds formatting, strict lint +and documentation checks; it does not claim to reproduce CI runtime coverage. + +## Common problems + +| Symptom | Next step | +| --- | --- | +| Linker or resource compiler missing | Check MSVC C++ tools and Windows SDK; use the MSVC toolchain | +| Building from Linux/WSL fails | Build on Windows; this repository intentionally uses Windows-only APIs | +| Release executable locked | Quit the repo-built app deliberately, or use a separate `CARGO_TARGET_DIR`; do not kill an unrelated runtime | +| Cargo waits for its build lock | Let the owning build finish; avoid concurrent builds sharing a target directory | +| Formatting failure | Run `cargo fmt --all`, then inspect the diff | +| `--locked` rejects dependency resolution | Check whether the task intentionally changed Cargo.toml; update and review the lockfile only for an intended dependency change | +| Smoke changes a running development session | Stop and use an isolated environment; its cleanup targets repo binaries and it writes real logs/tasks | +| Unit tests pass but tray/power behavior is uncertain | Use the specific manual row in the validation matrix and report the environment | + +## Keep the next change easy + +Update this map when an owner moves. Put a short module comment beside +non-obvious lifecycle boundaries and test pure decisions near their owner. +Keep measured evidence separate from intended contracts. A human should be +able to use the same instructions and review the same evidence as an agent. diff --git a/docs/MVP_VALIDATION_MATRIX.md b/docs/MVP_VALIDATION_MATRIX.md index c0555df..3372117 100644 --- a/docs/MVP_VALIDATION_MATRIX.md +++ b/docs/MVP_VALIDATION_MATRIX.md @@ -2,6 +2,11 @@ Use this as the MVP acceptance checklist for Wardoff as it exists today. It keeps automated coverage, manual spot checks, admin-only validation, and disruptive VM-only checks separate. +For everyday development, use the [local check commands](DEVELOPMENT.md#validation). +The smoke suite is state-changing: it stops repo-built Wardoff processes, starts +protection, writes real logs and may modify scheduled tasks. Use an isolated +Windows test environment even when not requesting a shutdown or suspend. + ## Scope and honesty rules This matrix covers only the current MVP surface: diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..06f5760 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,43 @@ +# Documentation + +Choose the page for your task. There is no need to read every document before a +small change. + +## Start here + +| Need | Read | +| --- | --- | +| Install and use Wardoff | [Project README](../README.md) | +| Set up a development environment | [Contributing](../CONTRIBUTING.md) | +| Find code, tests and common pitfalls | [Development guide](DEVELOPMENT.md) | +| Start an agent session | [Agent entrypoint](../AGENTS.md) | +| Understand current scope and priorities | [Plan](../PLAN.md) | + +## Maintained contracts + +| Topic | Owner | +| --- | --- | +| Architecture, threads and resource lifecycle | [Architecture](ARCHITECTURE.md) | +| Shutdown layers and Windows limits | [Shutdown layers](WINDOWS_SHUTDOWN_LAYERS.md) | +| Automated and manual acceptance | [Validation matrix](MVP_VALIDATION_MATRIX.md) | +| Why aggressive interception is excluded | [IFEO warning](IFEO_WARNING.md) | +| Sponsorship, signed binaries and paid support | [Business model](BUSINESS_MODEL.md) | +| Reporting security issues | [Security policy](../SECURITY.md) | +| Questions and support | [Support](../SUPPORT.md) | +| Released changes | [Changelog](../CHANGELOG.md) | + +## Evidence, comparisons and drafts + +These pages have their own date, scope or draft status. Re-check findings against +the code you are changing; they do not redefine product or architecture contracts. + +- [Security audit](SECURITY_AUDIT.md): findings and residual risks at the audited state. +- [Resource benchmark](benchmarks/2026-09-29-v0.2.0/README.md): protocol, raw data, + limitations and reproduction tools for one machine and binary. +- [Product comparison](COMPARISON.md): comparative background. +- [Release notes draft](RELEASE_NOTES_DRAFT.md): unpublished release copy. +- [Outreach drafts](OUTREACH_DRAFTS.md): unpublished community messages. + +Keep each rule in its owning document and update links when moving a page. +Claude and Copilot entrypoints route to the same agent guide; they are not +independent copies of project policy. diff --git a/scripts/check-docs.ps1 b/scripts/check-docs.ps1 new file mode 100644 index 0000000..e0bbc8d --- /dev/null +++ b/scripts/check-docs.ps1 @@ -0,0 +1,190 @@ +<# +.SYNOPSIS +Checks local Markdown destinations with a lightweight syntax scan. +.DESCRIPTION +External URLs and anchors are ignored, anchor existence is not validated, and this is not a full Markdown parser. +#> +[CmdletBinding()] +param( + [string]$RepoRoot +) + +$ErrorActionPreference = 'Stop' +$scriptDirectory = Split-Path -Parent $MyInvocation.MyCommand.Path +if ([string]::IsNullOrWhiteSpace($RepoRoot)) { + $RepoRoot = Split-Path -Parent $scriptDirectory +} +$resolvedRoot = (Resolve-Path -LiteralPath $RepoRoot).Path +$errors = [System.Collections.Generic.List[string]]::new() +$utf8Strict = New-Object System.Text.UTF8Encoding($false, $true) + +function Add-DocError { + param([string]$Message) + $script:errors.Add($Message) +} + +function Get-TargetPath { + param([string]$Target) + $value = $Target.Trim() + if ($value.StartsWith('<')) { + $endAngle = $value.IndexOf('>') + if ($endAngle -ge 0) { + $value = $value.Substring(0, $endAngle + 1) + } + } + if ($value.StartsWith('<') -and $value.EndsWith('>')) { + $value = $value.Substring(1, $value.Length - 2) + } + $titleMatch = [regex]::Match($value, '^(?.*?)(?:\s+["''][^"'']*["''])$') + if ($titleMatch.Success) { + $value = $titleMatch.Groups['destination'].Value + } + $value = $value.Trim() + if ([string]::IsNullOrWhiteSpace($value) -or $value.StartsWith('#') -or $value.StartsWith('//')) { + return $null + } + if ($value -match '^[A-Za-z][A-Za-z0-9+.-]*:') { + return $null + } + $fragmentIndex = $value.IndexOf('#') + if ($fragmentIndex -ge 0) { + $value = $value.Substring(0, $fragmentIndex) + } + if ([string]::IsNullOrWhiteSpace($value)) { + return $null + } + try { + return [Uri]::UnescapeDataString($value) + } catch { + return $value + } +} + +function Test-Target { + param( + [string]$SourcePath, + [int]$LineNumber, + [string]$Target + ) + $relativeTarget = Get-TargetPath $Target + if ($null -eq $relativeTarget) { + return + } + if ([IO.Path]::IsPathRooted($relativeTarget)) { + $candidate = $relativeTarget + } else { + $candidate = Join-Path (Split-Path -Parent $SourcePath) $relativeTarget + } + try { + $fullCandidate = [IO.Path]::GetFullPath($candidate) + } catch { + Add-DocError ("{0}:{1}: malformed local link target '{2}'" -f $SourcePath, $LineNumber, $Target) + return + } + if (-not (Test-Path -LiteralPath $fullCandidate)) { + Add-DocError ("{0}:{1}: missing local link target '{2}'" -f $SourcePath, $LineNumber, $Target) + } +} + +function Get-InlineTargets { + param([string]$Line) + $matches = [regex]::Matches($Line, '!?(?' -and $angle) { $angle = $false } + $position++ + } + if ($position -lt $Line.Length) { + $content = $Line.Substring($start, $position - $start).Trim() + if ($content.StartsWith('<')) { + $end = $content.IndexOf('>') + if ($end -ge 0) { $content = $content.Substring(0, $end + 1) } + } else { + $titleMatch = [regex]::Match($content, '^(?.*?)(?:\s+["''][^"'']*["''])$') + if ($titleMatch.Success) { $content = $titleMatch.Groups['destination'].Value } + } + [pscustomobject]@{ Target = $content } + } + } +} + +try { + $gitOutput = & git -C $resolvedRoot -c core.quotepath=false ls-files --cached --others --exclude-standard -- '*.md' 2>&1 + if ($LASTEXITCODE -ne 0) { + throw "Unable to enumerate Markdown files with Git: $($gitOutput -join ' ')" + } + $paths = @($gitOutput | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | ForEach-Object { $_.ToString().Trim() } | Sort-Object -Unique) + + foreach ($relativePath in $paths) { + $sourcePath = Join-Path $resolvedRoot ($relativePath -replace '/', '\') + if (-not (Test-Path -LiteralPath $sourcePath -PathType Leaf)) { + Add-DocError ("{0}: Markdown file listed by Git is missing" -f $sourcePath) + continue + } + try { + $bytes = [IO.File]::ReadAllBytes($sourcePath) + $text = $utf8Strict.GetString($bytes) + } catch { + Add-DocError ("{0}: invalid UTF-8 Markdown ({1})" -f $sourcePath, $_.Exception.Message) + continue + } + if ($text.IndexOf([char]0) -ge 0) { + Add-DocError ("{0}: Markdown contains embedded NUL" -f $sourcePath) + continue + } + $lines = $text -split "`r`n|`n|`r" + $inFence = $false + $fenceCharacter = $null + $fenceLength = 0 + for ($index = 0; $index -lt $lines.Count; $index++) { + $line = $lines[$index] + if (-not $inFence) { + $openingFence = [regex]::Match($line, '^\s{0,3}(?`{3,}|~{3,})') + if ($openingFence.Success) { + $inFence = $true + $fenceCharacter = $openingFence.Groups['marker'].Value.Substring(0, 1) + $fenceLength = $openingFence.Groups['marker'].Value.Length + continue + } + } else { + $closingFence = [regex]::Match($line, '^\s{0,3}(?`{3,}|~{3,})\s*$') + if ($closingFence.Success -and + $closingFence.Groups['marker'].Value.Substring(0, 1) -eq $fenceCharacter -and + $closingFence.Groups['marker'].Value.Length -ge $fenceLength) { + $inFence = $false + $fenceCharacter = $null + $fenceLength = 0 + } + continue + } + $parseLine = [regex]::Replace($line, '`+[^`]*`+', '') + foreach ($link in Get-InlineTargets $parseLine) { + Test-Target $sourcePath ($index + 1) $link.Target + } + $reference = [regex]::Match($parseLine, '^\s{0,3}\[[^\]]+\]:\s*(?<[^>\r\n]*>(?:\s+["''][^"'']*["''])?|.+?)\s*$') + if ($reference.Success) { + Test-Target $sourcePath ($index + 1) $reference.Groups['target'].Value + } + } + } + if ($errors.Count -gt 0) { + $errors | ForEach-Object { Write-Error $_ } + exit 1 + } + Write-Output ("Documentation checks passed ({0} Markdown files; local anchors are not validated)." -f $paths.Count) + exit 0 +} catch { + Write-Error $_.Exception.Message + exit 1 +} diff --git a/scripts/check.ps1 b/scripts/check.ps1 new file mode 100644 index 0000000..b5af51f --- /dev/null +++ b/scripts/check.ps1 @@ -0,0 +1,53 @@ +<# +.SYNOPSIS +Runs the repository's lightweight documentation and Cargo validation checks. +.DESCRIPTION +The Docs mode performs only the local Markdown scan; it does not validate anchors or parse full Markdown. +#> +[CmdletBinding()] +param( + [ValidateSet('Fast', 'Full', 'Docs')] + [string]$Mode = 'Fast' +) + +$ErrorActionPreference = 'Stop' +$repoRoot = (Resolve-Path -LiteralPath (Split-Path -Parent $PSScriptRoot)).Path +$originalLocation = (Get-Location).Path + +function Invoke-CheckStep { + param( + [string]$Label, + [scriptblock]$Action + ) + Write-Output ("==> {0}" -f $Label) + & $Action + if ($LASTEXITCODE -ne 0) { + throw ("{0} failed with exit code {1}." -f $Label, $LASTEXITCODE) + } + Write-Output ("PASS: {0}" -f $Label) +} + +try { + Set-Location -LiteralPath $repoRoot + $docsScript = Join-Path $PSScriptRoot 'check-docs.ps1' + Invoke-CheckStep 'Markdown documentation' { & powershell.exe -NoProfile -ExecutionPolicy Bypass -File $docsScript -RepoRoot $repoRoot } + if ($Mode -eq 'Docs') { + Write-Output 'Documentation checks completed.' + exit 0 + } + + Invoke-CheckStep 'cargo fmt --all -- --check' { & cargo fmt --all -- --check } + Invoke-CheckStep 'cargo check --locked' { & cargo check --locked } + Invoke-CheckStep 'cargo test --locked' { & cargo test --locked } + if ($Mode -eq 'Full') { + Invoke-CheckStep 'cargo clippy --locked --all-targets --all-features -- -D warnings' { & cargo clippy --locked --all-targets --all-features -- -D warnings } + Invoke-CheckStep 'cargo build --release --locked' { & cargo build --release --locked } + } + Write-Output ("{0} checks completed." -f $Mode) + exit 0 +} catch { + Write-Error $_.Exception.Message + exit 1 +} finally { + Set-Location -LiteralPath $originalLocation +} diff --git a/src/autostart.rs b/src/autostart.rs index b38401d..5be1053 100644 --- a/src/autostart.rs +++ b/src/autostart.rs @@ -677,7 +677,7 @@ fn sid_to_string(sid: PSID, context: &str) -> Result { } let string_sid = LocalAllocatedWideString(string_sid); - let sid = string_sid.to_string(); + let sid = string_sid.to_string_lossy(); if sid.is_empty() { return Err(format!( "Wardoff could not {context} before updating {AUTOSTART_TASK_PATH}: the process token resolved to an empty SID string." @@ -721,7 +721,7 @@ impl Drop for LocalSecurityDescriptor { struct LocalAllocatedWideString(PWSTR); impl LocalAllocatedWideString { - fn to_string(&self) -> String { + fn to_string_lossy(&self) -> String { if self.0.is_null() { return String::new(); } diff --git a/src/config.rs b/src/config.rs deleted file mode 100644 index 130be7e..0000000 --- a/src/config.rs +++ /dev/null @@ -1 +0,0 @@ -//! Configuration scaffolding intentionally removed. diff --git a/src/ipc.rs b/src/ipc.rs index d335da8..b5d0a6f 100644 --- a/src/ipc.rs +++ b/src/ipc.rs @@ -1,3 +1,9 @@ +//! Session-scoped control and read-only status transports. +//! +//! `IpcServer::start` owns startup of both pipe workers; requests are queued back +//! to the application thread, which owns mode changes. Keep wire types, access +//! checks and retry behavior consistent with docs/ARCHITECTURE.md. + use crate::blocker::BlockerMode; use crate::cli::StatusOutput; use crate::logger::{self, EventSource}; @@ -1149,8 +1155,7 @@ fn current_process_user_sid_string(control_pipe_path: &str) -> Result std::io::Result<()> { - let status_pipe_path = - current_status_pipe_path().map_err(|error| std::io::Error::other(error))?; + let status_pipe_path = current_status_pipe_path().map_err(std::io::Error::other)?; OpenOptions::new() .read(true) .open(status_pipe_path) diff --git a/src/main.rs b/src/main.rs index ec30e66..55a01fd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,16 @@ +//! Process dispatch and effectful runtime orchestration. +//! +//! Start at `run_main` for CLI dispatch, `bootstrap`/`run` for lifecycle, and +//! `Application` for IPC/tray effects. Pure decisions live in `runtime_policy`. +//! See docs/DEVELOPMENT.md for task-specific entrypoints and validation. + mod autostart; mod blocker; mod cli; -mod config; mod instance; mod ipc; mod logger; +mod runtime_policy; mod session_scope; mod tray; mod windows_util; @@ -18,9 +24,14 @@ use crate::instance::{ }; use crate::ipc::{ read_status, send_request, ClientError, IpcRequest, IpcResponse, IpcServer, PendingRequest, - PipeMode, IPC_WAKE_MESSAGE, + IPC_WAKE_MESSAGE, }; use crate::logger::EventSource; +use crate::runtime_policy::{ + ipc_request_for, pending_wake_restore_for_tray_power_action, + pending_wake_restore_ready_for_resume, resolved_autostart_state_for_tray, runtime_options_for, + PendingWakeRestore, RuntimeOptions, TraySurface, +}; use crate::tray::{ spawn_tray_service, TrayAction, TrayServiceHandle, TrayVisibility, TRAY_ACTION_WAKE_MESSAGE, }; @@ -44,6 +55,9 @@ use windows::Win32::UI::WindowsAndMessaging::{ const ELEVATED_RELAUNCH_MUTEX_RETRY_ATTEMPTS: usize = 20; const ELEVATED_RELAUNCH_MUTEX_RETRY_DELAY: Duration = Duration::from_millis(100); +// Callback bridge for the Win32 message loop on the application thread. `run` +// registers it only while its local Application is alive and clears callbacks +// before ordinary cleanup. Never dereference it from a background worker. static ACTIVE_APPLICATION: AtomicPtr = AtomicPtr::new(std::ptr::null_mut()); /// Coordinates CLI, tray, and blocker scaffolding for the Wardoff binary. @@ -58,38 +72,6 @@ struct Application { _primary_instance: InstanceGuard, } -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -enum TraySurface { - Visible, - Hidden, - Headless, -} - -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -struct RuntimeOptions { - initial_mode: BlockerMode, - tray_surface: TraySurface, -} - -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -enum PendingWakeRestore { - PendingSuspend(PowerAction), - WaitingForResume(PowerAction), -} - -impl PendingWakeRestore { - fn action(self) -> PowerAction { - match self { - PendingWakeRestore::PendingSuspend(action) - | PendingWakeRestore::WaitingForResume(action) => action, - } - } - - fn mark_system_suspended(self) -> Self { - PendingWakeRestore::WaitingForResume(self.action()) - } -} - /// Bootstraps logging, the central state manager, and the tray surface. fn bootstrap( primary_instance: InstanceGuard, @@ -521,50 +503,6 @@ where } } -fn runtime_options_for(action: RequestedAction) -> RuntimeOptions { - match action { - RequestedAction::Default => RuntimeOptions { - initial_mode: BlockerMode::Block, - tray_surface: TraySurface::Visible, - }, - RequestedAction::Block => RuntimeOptions { - initial_mode: BlockerMode::Block, - tray_surface: TraySurface::Headless, - }, - RequestedAction::Allow => RuntimeOptions { - initial_mode: BlockerMode::Allow, - tray_surface: TraySurface::Visible, - }, - RequestedAction::Hide => RuntimeOptions { - initial_mode: BlockerMode::Block, - tray_surface: TraySurface::Hidden, - }, - RequestedAction::Autostart { .. } => { - unreachable!("autostart changes do not start the primary runtime") - } - RequestedAction::Status | RequestedAction::Log { .. } => { - unreachable!("read-only CLI actions never start the runtime") - } - } -} - -fn ipc_request_for(action: RequestedAction) -> IpcRequest { - match action { - RequestedAction::Default | RequestedAction::Block | RequestedAction::Hide => { - IpcRequest::SetMode { - mode: PipeMode::Block, - } - } - RequestedAction::Allow => IpcRequest::SetMode { - mode: PipeMode::Allow, - }, - RequestedAction::Autostart { enabled } => IpcRequest::SetAutostart { enabled }, - RequestedAction::Status | RequestedAction::Log { .. } => { - unreachable!("read-only CLI actions do not use the control IPC request helper") - } - } -} - fn ensure_message_queue() { let mut message = MSG::default(); let _ = unsafe { PeekMessageW(&mut message, None, 0, 0, PM_NOREMOVE) }; @@ -1251,119 +1189,3 @@ fn runtime_request_label(action: RequestedAction) -> &'static str { RequestedAction::Status | RequestedAction::Log { .. } => "read-only", } } - -fn pending_wake_restore_for_tray_power_action( - previous_mode: BlockerMode, - action: PowerAction, -) -> Option { - match (previous_mode, action) { - (BlockerMode::Block, PowerAction::Sleep) => { - Some(PendingWakeRestore::PendingSuspend(PowerAction::Sleep)) - } - (BlockerMode::Block, PowerAction::Hibernate) => { - Some(PendingWakeRestore::PendingSuspend(PowerAction::Hibernate)) - } - _ => None, - } -} - -fn pending_wake_restore_ready_for_resume( - pending_restore: Option, -) -> Option { - // Windows can deliver a resume notification to this runtime without an earlier - // suspend broadcast reaching the hidden window, so any queued tray wake-restore - // must still restore Block mode on resume even if Wardoff never observed suspend. - match pending_restore { - Some(PendingWakeRestore::PendingSuspend(action)) - | Some(PendingWakeRestore::WaitingForResume(action)) => Some(action), - _ => None, - } -} - -fn resolved_autostart_state_for_tray( - previous_state: Option, - actual_state: Result, -) -> Result { - match actual_state { - Ok(actual_state) => Ok(actual_state), - Err(error) => previous_state.ok_or(error), - } -} - -#[cfg(test)] -mod tests { - use super::{ - pending_wake_restore_for_tray_power_action, pending_wake_restore_ready_for_resume, - resolved_autostart_state_for_tray, BlockerMode, PendingWakeRestore, PowerAction, - }; - - #[test] - fn prefers_current_autostart_state_when_available() { - assert_eq!( - resolved_autostart_state_for_tray(Some(false), Ok(true)), - Ok(true) - ); - } - - #[test] - fn falls_back_to_previous_autostart_state_when_reread_fails() { - assert_eq!( - resolved_autostart_state_for_tray(Some(false), Err("read failed".to_string())), - Ok(false) - ); - } - - #[test] - fn preserves_unknown_autostart_state_when_reads_fail() { - assert_eq!( - resolved_autostart_state_for_tray(None, Err("read failed".to_string())), - Err("read failed".to_string()) - ); - } - - #[test] - fn only_block_mode_sleep_and_hibernate_queue_restore_after_wake() { - assert_eq!( - pending_wake_restore_for_tray_power_action(BlockerMode::Block, PowerAction::Sleep), - Some(PendingWakeRestore::PendingSuspend(PowerAction::Sleep)) - ); - assert_eq!( - pending_wake_restore_for_tray_power_action(BlockerMode::Block, PowerAction::Hibernate), - Some(PendingWakeRestore::PendingSuspend(PowerAction::Hibernate)) - ); - assert_eq!( - pending_wake_restore_for_tray_power_action(BlockerMode::Allow, PowerAction::Sleep), - None - ); - assert_eq!( - pending_wake_restore_for_tray_power_action(BlockerMode::Block, PowerAction::Shutdown), - None - ); - } - - #[test] - fn pending_restore_marks_suspend_and_preserves_requested_action() { - let pending = PendingWakeRestore::PendingSuspend(PowerAction::Hibernate); - assert_eq!( - pending.mark_system_suspended(), - PendingWakeRestore::WaitingForResume(PowerAction::Hibernate) - ); - assert_eq!(pending.action(), PowerAction::Hibernate); - } - - #[test] - fn resume_restores_block_mode_even_if_suspend_was_not_observed() { - assert_eq!( - pending_wake_restore_ready_for_resume(Some(PendingWakeRestore::PendingSuspend( - PowerAction::Sleep - ))), - Some(PowerAction::Sleep) - ); - assert_eq!( - pending_wake_restore_ready_for_resume(Some(PendingWakeRestore::WaitingForResume( - PowerAction::Sleep - ))), - Some(PowerAction::Sleep) - ); - } -} diff --git a/src/runtime_policy.rs b/src/runtime_policy.rs new file mode 100644 index 0000000..7fbdf12 --- /dev/null +++ b/src/runtime_policy.rs @@ -0,0 +1,280 @@ +//! Pure decisions shared by startup and the running application. +//! +//! This module maps CLI intent and tray state without performing Windows effects. +//! Keep handles, threads, logging and Task Scheduler access in their existing owners. + +use crate::blocker::{BlockerMode, PowerAction}; +use crate::cli::RequestedAction; +use crate::ipc::{IpcRequest, PipeMode}; + +/// Selects whether startup creates a visible, hidden or absent tray surface. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub(crate) enum TraySurface { + /// Creates the normal visible tray icon. + Visible, + /// Keeps a tray service whose icon starts hidden. + Hidden, + /// Starts without a tray service. + Headless, +} + +/// Describes primary startup without starting a runtime. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub(crate) struct RuntimeOptions { + /// Mode to acquire during primary bootstrap. + pub(crate) initial_mode: BlockerMode, + /// Tray service and initial visibility to create. + pub(crate) tray_surface: TraySurface, +} + +/// Tracks a tray suspend request that should restore Block after resume. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub(crate) enum PendingWakeRestore { + /// A tray action queued restoration before a suspend notification arrived. + PendingSuspend(PowerAction), + /// Windows reported suspend; restoration still waits for resume. + WaitingForResume(PowerAction), +} + +impl PendingWakeRestore { + /// Returns the power action whose Block state should be restored. + pub(crate) fn action(self) -> PowerAction { + match self { + PendingWakeRestore::PendingSuspend(action) + | PendingWakeRestore::WaitingForResume(action) => action, + } + } + + /// Records a suspend notification without changing the requested action. + pub(crate) fn mark_system_suspended(self) -> Self { + PendingWakeRestore::WaitingForResume(self.action()) + } +} + +/// Maps runtime-launching actions; read-only and autostart actions must be dispatched earlier. +pub(crate) fn runtime_options_for(action: RequestedAction) -> RuntimeOptions { + match action { + RequestedAction::Default => RuntimeOptions { + initial_mode: BlockerMode::Block, + tray_surface: TraySurface::Visible, + }, + RequestedAction::Block => RuntimeOptions { + initial_mode: BlockerMode::Block, + tray_surface: TraySurface::Headless, + }, + RequestedAction::Allow => RuntimeOptions { + initial_mode: BlockerMode::Allow, + tray_surface: TraySurface::Visible, + }, + RequestedAction::Hide => RuntimeOptions { + initial_mode: BlockerMode::Block, + tray_surface: TraySurface::Hidden, + }, + RequestedAction::Autostart { .. } => { + unreachable!("autostart changes do not start the primary runtime") + } + RequestedAction::Status | RequestedAction::Log { .. } => { + unreachable!("read-only CLI actions never start the runtime") + } + } +} + +/// Maps secondary control actions; read-only actions must be dispatched earlier. +pub(crate) fn ipc_request_for(action: RequestedAction) -> IpcRequest { + match action { + RequestedAction::Default | RequestedAction::Block | RequestedAction::Hide => { + IpcRequest::SetMode { + mode: PipeMode::Block, + } + } + RequestedAction::Allow => IpcRequest::SetMode { + mode: PipeMode::Allow, + }, + RequestedAction::Autostart { enabled } => IpcRequest::SetAutostart { enabled }, + RequestedAction::Status | RequestedAction::Log { .. } => { + unreachable!("read-only CLI actions do not use the control IPC request helper") + } + } +} + +/// Queues wake restoration only when a Block-mode tray action requests suspend. +pub(crate) fn pending_wake_restore_for_tray_power_action( + previous_mode: BlockerMode, + action: PowerAction, +) -> Option { + match (previous_mode, action) { + (BlockerMode::Block, PowerAction::Sleep) => { + Some(PendingWakeRestore::PendingSuspend(PowerAction::Sleep)) + } + (BlockerMode::Block, PowerAction::Hibernate) => { + Some(PendingWakeRestore::PendingSuspend(PowerAction::Hibernate)) + } + _ => None, + } +} + +/// Accepts resume even if Windows did not deliver the earlier suspend notification. +pub(crate) fn pending_wake_restore_ready_for_resume( + pending_restore: Option, +) -> Option { + // Windows can deliver a resume notification to this runtime without an earlier + // suspend broadcast reaching the hidden window, so any queued tray wake-restore + // must still restore Block mode on resume even if Wardoff never observed suspend. + match pending_restore { + Some(PendingWakeRestore::PendingSuspend(action)) + | Some(PendingWakeRestore::WaitingForResume(action)) => Some(action), + _ => None, + } +} + +/// Prefers the current task state, retaining the previous checkbox on a read failure. +pub(crate) fn resolved_autostart_state_for_tray( + previous_state: Option, + actual_state: Result, +) -> Result { + match actual_state { + Ok(actual_state) => Ok(actual_state), + Err(error) => previous_state.ok_or(error), + } +} + +#[cfg(test)] +mod tests { + use super::{ + ipc_request_for, pending_wake_restore_for_tray_power_action, + pending_wake_restore_ready_for_resume, resolved_autostart_state_for_tray, + runtime_options_for, BlockerMode, IpcRequest, PendingWakeRestore, PipeMode, PowerAction, + RequestedAction, RuntimeOptions, TraySurface, + }; + + #[test] + fn startup_preserves_visible_hidden_and_headless_modes() { + for (action, initial_mode, tray_surface) in [ + ( + RequestedAction::Default, + BlockerMode::Block, + TraySurface::Visible, + ), + ( + RequestedAction::Block, + BlockerMode::Block, + TraySurface::Headless, + ), + ( + RequestedAction::Allow, + BlockerMode::Allow, + TraySurface::Visible, + ), + ( + RequestedAction::Hide, + BlockerMode::Block, + TraySurface::Hidden, + ), + ] { + assert_eq!( + runtime_options_for(action), + RuntimeOptions { + initial_mode, + tray_surface + } + ); + } + } + + #[test] + fn secondary_commands_preserve_control_protocol_mapping() { + for action in [ + RequestedAction::Default, + RequestedAction::Block, + RequestedAction::Hide, + ] { + assert!(matches!( + ipc_request_for(action), + IpcRequest::SetMode { + mode: PipeMode::Block + } + )); + } + assert!(matches!( + ipc_request_for(RequestedAction::Allow), + IpcRequest::SetMode { + mode: PipeMode::Allow + } + )); + for enabled in [false, true] { + assert!( + matches!(ipc_request_for(RequestedAction::Autostart { enabled }), IpcRequest::SetAutostart { enabled: actual } if actual == enabled) + ); + } + } + + #[test] + fn prefers_current_autostart_state_when_available() { + assert_eq!( + resolved_autostart_state_for_tray(Some(false), Ok(true)), + Ok(true) + ); + } + + #[test] + fn falls_back_to_previous_autostart_state_when_reread_fails() { + assert_eq!( + resolved_autostart_state_for_tray(Some(false), Err("read failed".to_string())), + Ok(false) + ); + } + + #[test] + fn preserves_unknown_autostart_state_when_reads_fail() { + assert_eq!( + resolved_autostart_state_for_tray(None, Err("read failed".to_string())), + Err("read failed".to_string()) + ); + } + + #[test] + fn only_block_mode_sleep_and_hibernate_queue_restore_after_wake() { + assert_eq!( + pending_wake_restore_for_tray_power_action(BlockerMode::Block, PowerAction::Sleep), + Some(PendingWakeRestore::PendingSuspend(PowerAction::Sleep)) + ); + assert_eq!( + pending_wake_restore_for_tray_power_action(BlockerMode::Block, PowerAction::Hibernate), + Some(PendingWakeRestore::PendingSuspend(PowerAction::Hibernate)) + ); + assert_eq!( + pending_wake_restore_for_tray_power_action(BlockerMode::Allow, PowerAction::Sleep), + None + ); + assert_eq!( + pending_wake_restore_for_tray_power_action(BlockerMode::Block, PowerAction::Shutdown), + None + ); + } + + #[test] + fn pending_restore_marks_suspend_and_preserves_requested_action() { + let pending = PendingWakeRestore::PendingSuspend(PowerAction::Hibernate); + assert_eq!( + pending.mark_system_suspended(), + PendingWakeRestore::WaitingForResume(PowerAction::Hibernate) + ); + assert_eq!(pending.action(), PowerAction::Hibernate); + } + + #[test] + fn resume_restores_block_mode_even_if_suspend_was_not_observed() { + assert_eq!( + pending_wake_restore_ready_for_resume(Some(PendingWakeRestore::PendingSuspend( + PowerAction::Sleep + ))), + Some(PowerAction::Sleep) + ); + assert_eq!( + pending_wake_restore_ready_for_resume(Some(PendingWakeRestore::WaitingForResume( + PowerAction::Sleep + ))), + Some(PowerAction::Sleep) + ); + } +} diff --git a/tests/developer_checks.ps1 b/tests/developer_checks.ps1 new file mode 100644 index 0000000..82b1531 --- /dev/null +++ b/tests/developer_checks.ps1 @@ -0,0 +1,153 @@ +[CmdletBinding()] +param() + +$ErrorActionPreference = 'Stop' +$checker = Join-Path $PSScriptRoot '..\scripts\check-docs.ps1' +$entrypoint = Join-Path $PSScriptRoot '..\scripts\check.ps1' +$fixtureRoot = Join-Path ([IO.Path]::GetTempPath()) ('wardoff-doc-check-' + [Guid]::NewGuid().ToString('N')) + +function Invoke-FixtureChecker { + param([switch]$ExpectFailure) + + $previousErrorAction = $ErrorActionPreference + $ErrorActionPreference = 'Continue' + try { + $result = & powershell.exe -NoProfile -ExecutionPolicy Bypass -File $checker -RepoRoot $fixtureRoot 2>&1 + } finally { + $ErrorActionPreference = $previousErrorAction + } + $code = $LASTEXITCODE + if ($ExpectFailure) { + if ($code -eq 0) { throw "Expected checker failure, but it succeeded.`n$($result -join [Environment]::NewLine)" } + } elseif ($code -ne 0) { + throw "Expected checker success, exit code $code.`n$($result -join [Environment]::NewLine)" + } + return ($result -join [Environment]::NewLine) +} + +function Assert-Contains { + param([string]$Text, [string]$Expected) + if ($Text.IndexOf($Expected, [StringComparison]::OrdinalIgnoreCase) -lt 0) { + throw "Expected output to contain '$Expected'.`n$Text" + } +} + +function Invoke-Entrypoint { + param( + [ValidateSet('Docs', 'Full')] + [string]$Mode, + [switch]$ExpectFailure + ) + $previousErrorAction = $ErrorActionPreference + $ErrorActionPreference = 'Continue' + try { + $result = & powershell.exe -NoProfile -ExecutionPolicy Bypass -File $entrypoint -Mode $Mode 2>&1 + } finally { + $ErrorActionPreference = $previousErrorAction + } + $code = $LASTEXITCODE + if ($ExpectFailure -and $code -eq 0) { throw "Expected $Mode entrypoint failure, but it succeeded." } + if (-not $ExpectFailure -and $code -ne 0) { throw "Expected $Mode entrypoint success, exit code $code.`n$($result -join [Environment]::NewLine)" } + return ($result -join [Environment]::NewLine) +} + +try { + New-Item -ItemType Directory -Path $fixtureRoot -Force | Out-Null + Push-Location $fixtureRoot + git init --quiet + Set-Content -LiteralPath (Join-Path $fixtureRoot 'README.md') -Encoding utf8 -Value @' +# Fixture + +[existing](docs/guide%20file.md) +[reference][guide] +[remote](https://example.invalid/no-file) +[anchor](#section) +`[inline-code](missing-inline.md)` + +````text +[fenced-code](missing-fenced.md) +``` +[still-fenced](missing-fenced-2.md) +```` + +[guide]: "optional title" +[bare]: docs/guide%20file.md "optional title" +'@ + New-Item -ItemType Directory -Path (Join-Path $fixtureRoot 'docs') | Out-Null + Set-Content -LiteralPath (Join-Path $fixtureRoot 'docs\guide file.md') -Encoding utf8 -Value '# Guide' + Set-Content -LiteralPath (Join-Path $fixtureRoot 'docs\nötes.md') -Encoding utf8 -Value '# Notes' + Set-Content -LiteralPath (Join-Path $fixtureRoot 'ignored.md') -Encoding utf8 -Value '[missing](nope.md)' + Add-Content -LiteralPath (Join-Path $fixtureRoot '.gitignore') -Encoding utf8 -Value 'ignored.md' + git add README.md 'docs/guide file.md' 'docs/nötes.md' .gitignore + + $success = Invoke-FixtureChecker + Assert-Contains $success 'Documentation checks passed' + + $cargoPath = Join-Path $fixtureRoot 'cargo.cmd' + $cargoTrace = Join-Path $fixtureRoot 'cargo-trace.txt' + Set-Content -LiteralPath $cargoPath -Encoding ascii -Value '@echo off', 'echo %*>>"%~dp0cargo-trace.txt"', 'exit /b 7' + $oldPath = $env:PATH + try { + $env:PATH = $fixtureRoot + ';' + $oldPath + $docsEntry = Invoke-Entrypoint -Mode Docs + Assert-Contains $docsEntry 'Documentation checks completed.' + if (Test-Path -LiteralPath $cargoTrace) { throw 'Docs mode unexpectedly invoked Cargo.' } + $fullEntry = Invoke-Entrypoint -Mode Full -ExpectFailure + Assert-Contains $fullEntry 'cargo fmt --all -- --check failed' + $trace = Get-Content -LiteralPath $cargoTrace -Raw + Assert-Contains $trace 'fmt --all -- --check' + if ($trace -match '(?:^|\s)(?:check|test|clippy|build)(?:\s|$)') { throw "Full mode ran steps after the first failure: $trace" } + } finally { + $env:PATH = $oldPath + } + Remove-Item -LiteralPath $cargoPath, $cargoTrace -Force -ErrorAction SilentlyContinue + + Set-Content -LiteralPath (Join-Path $fixtureRoot 'untracked.md') -Encoding utf8 -Value '[missing](untracked-target.md)' + $untrackedFailure = Invoke-FixtureChecker -ExpectFailure + Assert-Contains $untrackedFailure 'untracked.md:1' + + Remove-Item -LiteralPath (Join-Path $fixtureRoot 'untracked.md') + Set-Content -LiteralPath (Join-Path $fixtureRoot 'broken.md') -Encoding utf8 -Value '[missing](does-not-exist.md)' + $brokenFailure = Invoke-FixtureChecker -ExpectFailure + Assert-Contains $brokenFailure 'broken.md:1' + Assert-Contains $brokenFailure 'does-not-exist.md' + + Remove-Item -LiteralPath (Join-Path $fixtureRoot 'broken.md') + Set-Content -LiteralPath (Join-Path $fixtureRoot 'broken-reference.md') -Encoding utf8 -Value @' +[reference][missing] + +[missing]: missing-reference.md +'@ + $brokenReferenceFailure = Invoke-FixtureChecker -ExpectFailure + Assert-Contains $brokenReferenceFailure 'broken-reference.md:3' + Assert-Contains $brokenReferenceFailure 'missing-reference.md' + + Remove-Item -LiteralPath (Join-Path $fixtureRoot 'broken-reference.md') + $invalidPath = Join-Path $fixtureRoot 'invalid.md' + [IO.File]::WriteAllBytes($invalidPath, [byte[]](0x23, 0x20, 0xC3, 0x28)) + $invalidFailure = Invoke-FixtureChecker -ExpectFailure + Assert-Contains $invalidFailure 'invalid.md' + Assert-Contains $invalidFailure 'UTF-8' + + Remove-Item -LiteralPath $invalidPath + $nulPath = Join-Path $fixtureRoot 'nul.md' + [IO.File]::WriteAllBytes($nulPath, [byte[]](0x23, 0x20, 0x41, 0x00, 0x42)) + $nulFailure = Invoke-FixtureChecker -ExpectFailure + Assert-Contains $nulFailure 'nul.md' + Assert-Contains $nulFailure 'NUL' + + Write-Output 'Developer checks tests passed.' +} finally { + Pop-Location -ErrorAction SilentlyContinue + $resolvedFixture = $null + if (Test-Path -LiteralPath $fixtureRoot) { + $resolvedFixture = (Resolve-Path -LiteralPath $fixtureRoot).Path + } + $tempRoot = [IO.Path]::GetFullPath([IO.Path]::GetTempPath()).TrimEnd('\') + $fixtureName = Split-Path -Leaf $resolvedFixture + if ($resolvedFixture -and $resolvedFixture.StartsWith($tempRoot + '\', [StringComparison]::OrdinalIgnoreCase) -and + $fixtureName.StartsWith('wardoff-doc-check-', [StringComparison]::OrdinalIgnoreCase)) { + Remove-Item -LiteralPath $fixtureRoot -Recurse -Force + } +} +exit 0 diff --git a/tests/smoke_test.ps1 b/tests/smoke_test.ps1 index da45c0d..2f5dde9 100644 --- a/tests/smoke_test.ps1 +++ b/tests/smoke_test.ps1 @@ -1,3 +1,12 @@ +<# +.SYNOPSIS +State-changing Windows integration tests; use an isolated test environment. +.DESCRIPTION +Stops repo-built Wardoff processes (including a manually started development +instance), runs Block/Allow, writes real user logs and can change scheduled tasks +when elevated. For routine development use scripts/check.ps1 instead. +See docs/MVP_VALIDATION_MATRIX.md for coverage and manual acceptance. +#> [CmdletBinding()] param() From ade2f6a8c8adb307b8cb617fe4d30ea115d573ed Mon Sep 17 00:00:00 2001 From: RomainROCH Date: Sun, 4 Oct 2026 21:04:08 +0200 Subject: [PATCH 07/15] fix: bound log tail reads and preserve runtime cleanup --- PLAN.md | 2 +- README.md | 5 +- docs/ARCHITECTURE.md | 12 +++- src/cli.rs | 5 +- src/logger/mod.rs | 90 ++++++++++++++++++++++++++++-- src/main.rs | 127 ++++++++++++++++++++++++++++++++++++++++-- tests/log_tail_cli.rs | 124 +++++++++++++++++++++++++++++++++++++++++ 7 files changed, 350 insertions(+), 15 deletions(-) create mode 100644 tests/log_tail_cli.rs diff --git a/PLAN.md b/PLAN.md index 11051c0..956324b 100644 --- a/PLAN.md +++ b/PLAN.md @@ -57,7 +57,7 @@ Keep improving confidence in the features that already exist: - tighten documentation around actual runtime behavior and limits - improve manual validation guidance for shutdown, sign-out, sleep, hibernate, remote shutdown, autostart, and elevation-sensitive paths - fix bugs or rough edges in tray, IPC, logging, and Task Scheduler flows -- reduce the current roughly 4-second inactive `wardoff --status` path caused by sequential IPC retry loops +- reduce status fallback latency when the session mutex is occupied but both IPC endpoints are unavailable (up to roughly four seconds of sequential retries); the ordinary inactive path already returns without pipe retries - keep admin versus non-admin behavior explicit - keep LTSC-style Layer 3 skip behavior documented as normal when the UpdateOrchestrator reboot task is absent diff --git a/README.md b/README.md index 2ff5e78..6452af8 100644 --- a/README.md +++ b/README.md @@ -163,8 +163,9 @@ wardoff --version - `wardoff --status` prints compact JSON for scripts - `wardoff --log` prints recent structured log entries - `wardoff --log --tail 10` prints the newest 10 structured log entries +- `--tail` accepts 0 through 100000 lines; larger values return a readable error without allocating the requested capacity - `wardoff --autostart on|off` enables or disables the scheduled-task autostart entry -- `wardoff --version` prints the package version, for example `wardoff 0.1.0` +- `wardoff --version` prints the package version as `wardoff ` - direct PowerShell invocations of `wardoff --help`, `--version`, `--status`, and `--log --tail N` now behave like normal console commands with real stdout, stderr, and exit codes Read-only commands stay non-elevated: @@ -173,7 +174,7 @@ Read-only commands stay non-elevated: - `--status` reads status through the dedicated session-scoped `WardoffStatus` named pipe when a primary runtime is active - `--log` and `--log --tail N` read the rotating JSONL log files directly - those read-only commands stay attached to the calling shell so direct PowerShell CLI invocations can capture their output inline -- today, `--status` can still take a few seconds to return `{"state":"inactive"}` when no primary runtime is running; when a runtime is active, the status-pipe path should return quickly +- when no primary runtime owns the session mutex, `--status` returns `{"state":"inactive"}` without retrying the pipes; if the mutex is occupied but both IPC endpoints are unavailable, the fallback retries can still take about four seconds State-changing/default behavior uses the normal runtime path: diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 128db58..467fab4 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -42,6 +42,12 @@ Owns application bootstrap and shutdown: This is the best entry point for understanding the whole runtime. +`run_with_cleanup` always executes shutdown after the message loop returns, +including a failed `WaitMessage`. It unregisters both callbacks and clears the +application pointer before releasing runtime resources. If the loop and shutdown +both fail, the returned error retains both causes. This closes the former early +return that could leave callbacks pointing at a dropped application. + ### `src/runtime_policy.rs` (intentional responsibility extraction) Pure decisions formerly embedded in `main.rs` live together with their unit tests: @@ -80,6 +86,10 @@ Contains the background tray thread and tray UI state management. - file rotation - recent-log tail reading for CLI output +Tail requests are bounded by the [CLI contract](../README.md#usage). +The reader grows its line buffer as it encounters records and uses fallible +reservation, rather than allocating a caller-supplied capacity before reading. + ### `src/ipc.rs` Contains the named-pipe IPC transport used by secondary commands to talk to the primary runtime. @@ -270,7 +280,7 @@ The control pipe is now created with explicit local-only security instead of the - a medium-integrity mandatory label allows the same interactive Windows user to send control commands to an elevated primary runtime - if a caller still hits access denied, Wardoff maps that to a clear product message instead of returning a raw Win32 pipe error -At the moment, the inactive `wardoff --status` path is intentionally conservative but slow: when no primary runtime is present, the client first retries the read-only status pipe and then retries the control pipe before concluding that Wardoff is inactive. Each pipe path currently uses 20 attempts with a 100 ms delay, so the fully inactive path accumulates to roughly 4 seconds before returning `{"state":"inactive"}`. This current delay comes from the sequential named-pipe retry loops, not from UAC. +`handle_status_request` first probes the session mutex. If it can claim the primary instance, it releases that temporary claim and returns `{"state":"inactive"}` without connecting to either pipe. When the mutex is already occupied, it tries the read-only status pipe and then the control fallback. If both endpoints are unavailable, their sequential retries (20 attempts with a 100 ms delay per pipe) can still take roughly four seconds. That delay belongs to the occupied-mutex fallback, not the ordinary inactive path or UAC. ### Why the UI thread handles requests diff --git a/src/cli.rs b/src/cli.rs index 108b5bb..84914d8 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -73,7 +73,7 @@ pub struct WardoffCli { /// Prints the newest structured Wardoff JSON log lines. #[arg(long, conflicts_with_all = ["block", "allow", "status", "hide", "autostart"])] log: bool, - /// Overrides the default number of structured log lines shown by `--log`. + /// Overrides the default number of structured log lines shown by `--log` (maximum 100000). #[arg(long, value_name = "N", requires = "log")] tail: Option, /// Internal marker used to prevent default-launch elevation loops. @@ -237,6 +237,9 @@ mod tests { assert!(error .to_string() .contains("Open-source Windows shutdown/reboot protection and idle-sleep prevention")); + assert!(error + .to_string() + .contains(&format!("maximum {}", crate::logger::MAX_TAIL_LINE_COUNT))); } #[test] diff --git a/src/logger/mod.rs b/src/logger/mod.rs index 0823db1..643de38 100644 --- a/src/logger/mod.rs +++ b/src/logger/mod.rs @@ -19,6 +19,8 @@ use windows::Win32::Storage::FileSystem::{ const DEFAULT_LOG_FILE_NAME: &str = "wardoff.jsonl"; const WRITER_THREAD_NAME: &str = "wardoff-structured-logger"; const DEFAULT_TAIL_LINE_COUNT: usize = 20; +/// Bounds retained line bookkeeping independently of a caller's requested capacity. +pub(crate) const MAX_TAIL_LINE_COUNT: usize = 100_000; const MAX_LOG_BYTES: u64 = 5 * 1024 * 1024; const MAX_LOG_FILES: usize = 3; @@ -166,13 +168,23 @@ pub(crate) fn default_tail_line_count() -> usize { /// Reads the newest structured log lines across the retained rotation set. pub(crate) fn read_recent_lines(limit: usize) -> Result, String> { + read_recent_lines_from_path(&default_log_path(), limit) +} + +fn read_recent_lines_from_path(base_path: &Path, limit: usize) -> Result, String> { + if limit > MAX_TAIL_LINE_COUNT { + return Err(format!( + "The --tail value cannot exceed {MAX_TAIL_LINE_COUNT} lines." + )); + } + if limit == 0 { return Ok(Vec::new()); } - let mut recent_lines = VecDeque::with_capacity(limit); + let mut recent_lines = VecDeque::new(); - for path in log_paths_oldest_to_newest(default_log_path()) { + for path in log_paths_oldest_to_newest(base_path) { if log_file_metadata_if_exists(&path)?.is_none() { continue; } @@ -186,6 +198,13 @@ pub(crate) fn read_recent_lines(limit: usize) -> Result, String> { if recent_lines.len() == limit { let _ = recent_lines.pop_front(); + } else { + recent_lines.try_reserve(1).map_err(|error| { + format!( + "Wardoff could not reserve memory for recent log lines while reading {}: {error}", + path.display() + ) + })?; } recent_lines.push_back(line); } @@ -479,14 +498,14 @@ fn rotated_log_path(base_path: &Path, index: usize) -> PathBuf { parent.join(format!("{stem}.{index}.{extension}")) } -fn log_paths_oldest_to_newest(base_path: PathBuf) -> Vec { +fn log_paths_oldest_to_newest(base_path: &Path) -> Vec { let mut paths = Vec::with_capacity(MAX_LOG_FILES); for index in (1..MAX_LOG_FILES).rev() { - paths.push(rotated_log_path(&base_path, index)); + paths.push(rotated_log_path(base_path, index)); } - paths.push(base_path); + paths.push(base_path.to_path_buf()); paths } @@ -542,6 +561,67 @@ mod tests { fs::remove_dir_all(base).unwrap(); } + #[test] + fn read_recent_lines_rejects_tail_above_limit_before_filesystem_access() { + let missing_path = unique_test_root("tail-limit-before-filesystem").join("wardoff.jsonl"); + + for limit in [MAX_TAIL_LINE_COUNT + 1, usize::MAX] { + let error = read_recent_lines_from_path(&missing_path, limit).unwrap_err(); + assert!(error.contains("--tail")); + assert!(error.contains(&MAX_TAIL_LINE_COUNT.to_string())); + } + } + + #[test] + fn read_recent_lines_zero_returns_empty_without_filesystem_access() { + let missing_path = unique_test_root("zero-tail-without-filesystem").join("wardoff.jsonl"); + + assert!(read_recent_lines_from_path(&missing_path, 0) + .unwrap() + .is_empty()); + } + + #[test] + fn read_recent_lines_returns_exact_limit_from_existing_file() { + let base = unique_test_root("exact-tail-limit"); + fs::create_dir_all(&base).unwrap(); + let log_path = base.join(DEFAULT_LOG_FILE_NAME); + fs::write(&log_path, "one\ntwo\nthree\n").unwrap(); + + let lines = read_recent_lines_from_path(&log_path, 2).unwrap(); + + assert_eq!(lines, ["two", "three"]); + assert_eq!( + read_recent_lines_from_path(&log_path, MAX_TAIL_LINE_COUNT).unwrap(), + ["one", "two", "three"] + ); + fs::remove_dir_all(base).unwrap(); + } + + #[test] + fn read_recent_lines_ignores_absent_files() { + let missing_path = unique_test_root("absent-log-files").join(DEFAULT_LOG_FILE_NAME); + + assert!(read_recent_lines_from_path(&missing_path, 20) + .unwrap() + .is_empty()); + } + + #[test] + fn read_recent_lines_preserves_rotation_order_and_handles_crlf() { + let base = unique_test_root("rotation-order"); + fs::create_dir_all(&base).unwrap(); + let log_path = base.join(DEFAULT_LOG_FILE_NAME); + fs::write(rotated_log_path(&log_path, 2), "one\r\ntwo\r\n").unwrap(); + fs::write(rotated_log_path(&log_path, 1), "three\r\nfour\r\n").unwrap(); + fs::write(&log_path, "five\r\nsix\r\n").unwrap(); + + let lines = read_recent_lines_from_path(&log_path, 4).unwrap(); + + assert_eq!(lines, ["three", "four", "five", "six"]); + fs::remove_dir_all(base).unwrap(); + } + fn unique_test_root(name: &str) -> PathBuf { let unique = SystemTime::now() .duration_since(UNIX_EPOCH) diff --git a/src/main.rs b/src/main.rs index 55a01fd..76d3926 100644 --- a/src/main.rs +++ b/src/main.rs @@ -139,12 +139,44 @@ fn bootstrap( Ok(application) } +/// Runs an operation and always runs its cleanup, preserving both failures when needed. +fn run_with_cleanup( + state: &mut T, + loop_fn: Loop, + cleanup_fn: Cleanup, +) -> Result<(), Box> +where + Loop: FnOnce(&mut T) -> Result<(), Box>, + Cleanup: FnOnce(&mut T) -> Result<(), Box>, +{ + let loop_result = loop_fn(state); + let cleanup_result = cleanup_fn(state); + + match (loop_result, cleanup_result) { + (Ok(()), Ok(())) => Ok(()), + (Err(loop_error), Ok(())) => Err(loop_error), + (Ok(()), Err(cleanup_error)) => Err(cleanup_error), + (Err(loop_error), Err(cleanup_error)) => Err(Box::new(other_error(format!( + "runtime loop failed: {loop_error}; cleanup failed: {cleanup_error}" + )))), + } +} + /// Runs the shared Win32 event loop for the hidden Layer 1 windows and the tray icon. fn run(mut application: Application) -> Result<(), Box> { ACTIVE_APPLICATION.store(&mut application as *mut Application, Ordering::Release); blocker::shutdown::set_end_session_cleanup_callback(forced_shutdown_cleanup_callback); blocker::shutdown::set_power_broadcast_callback(power_broadcast_callback); + run_with_cleanup(&mut application, run_message_loop, |application| { + blocker::shutdown::clear_end_session_cleanup_callback(); + blocker::shutdown::clear_power_broadcast_callback(); + ACTIVE_APPLICATION.store(std::ptr::null_mut(), Ordering::Release); + application.shutdown() + }) +} + +fn run_message_loop(application: &mut Application) -> Result<(), Box> { let mut message = MSG::default(); 'message_loop: loop { @@ -179,11 +211,6 @@ fn run(mut application: Application) -> Result<(), Box> { unsafe { WaitMessage()? }; } - - blocker::shutdown::clear_end_session_cleanup_callback(); - blocker::shutdown::clear_power_broadcast_callback(); - ACTIVE_APPLICATION.store(std::ptr::null_mut(), Ordering::Release); - application.shutdown()?; Ok(()) } @@ -1189,3 +1216,93 @@ fn runtime_request_label(action: RequestedAction) -> &'static str { RequestedAction::Status | RequestedAction::Log { .. } => "read-only", } } + +#[cfg(test)] +mod tests { + use super::run_with_cleanup; + use std::error::Error; + use std::io; + + #[test] + fn run_with_cleanup_returns_success_after_normal_loop_and_cleanup() { + let mut events = Vec::new(); + + let result = run_with_cleanup( + &mut events, + |events| { + events.push("loop"); + Ok::<(), Box>(()) + }, + |events| { + events.push("cleanup"); + Ok::<(), Box>(()) + }, + ); + + assert!(result.is_ok()); + assert_eq!(events, ["loop", "cleanup"]); + } + + #[test] + fn run_with_cleanup_runs_cleanup_after_loop_error() { + let mut events = Vec::new(); + + let result = run_with_cleanup( + &mut events, + |events| { + events.push("loop"); + Err::<(), Box>(Box::new(io::Error::other("loop error"))) + }, + |events| { + events.push("cleanup"); + Ok::<(), Box>(()) + }, + ); + + assert_eq!(result.unwrap_err().to_string(), "loop error"); + assert_eq!(events, ["loop", "cleanup"]); + } + + #[test] + fn run_with_cleanup_propagates_shutdown_error() { + let mut events = Vec::new(); + + let result = run_with_cleanup( + &mut events, + |events| { + events.push("loop"); + Ok::<(), Box>(()) + }, + |events| { + events.push("shutdown"); + Err::<(), Box>(Box::new(io::Error::other("shutdown error"))) + }, + ); + + assert_eq!(result.unwrap_err().to_string(), "shutdown error"); + assert_eq!(events, ["loop", "shutdown"]); + } + + #[test] + fn run_with_cleanup_preserves_both_errors_in_order() { + let mut events = Vec::new(); + + let result = run_with_cleanup( + &mut events, + |events| { + events.push("loop"); + Err::<(), Box>(Box::new(io::Error::other("loop error"))) + }, + |events| { + events.push("shutdown"); + Err::<(), Box>(Box::new(io::Error::other("shutdown error"))) + }, + ); + + assert_eq!( + result.unwrap_err().to_string(), + "runtime loop failed: loop error; cleanup failed: shutdown error" + ); + assert_eq!(events, ["loop", "shutdown"]); + } +} diff --git a/tests/log_tail_cli.rs b/tests/log_tail_cli.rs new file mode 100644 index 0000000..5cedb88 --- /dev/null +++ b/tests/log_tail_cli.rs @@ -0,0 +1,124 @@ +#![cfg(windows)] + +use std::fs; +use std::path::PathBuf; +use std::process::{Command, Output}; +use std::time::{SystemTime, UNIX_EPOCH}; + +struct TempFixture { + root: PathBuf, +} + +impl TempFixture { + fn new(name: &str) -> Self { + let unique = SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("system clock should be after the Unix epoch") + .as_nanos(); + let root = std::env::temp_dir().join(format!("wardoff-log-tail-{name}-{unique}")); + fs::create_dir_all(root.join("Wardoff").join("logs")).expect("create log fixture root"); + Self { root } + } + + fn log_path(&self) -> PathBuf { + self.root.join("Wardoff").join("logs").join("wardoff.jsonl") + } + + fn write_log(&self, name: &str, contents: &str) { + let path = if name == "wardoff.jsonl" { + self.log_path() + } else { + self.log_path() + .parent() + .expect("log path should have a parent") + .join(name) + }; + fs::write(path, contents).expect("write log fixture"); + } +} + +impl Drop for TempFixture { + fn drop(&mut self) { + let _ = fs::remove_dir_all(&self.root); + } +} + +fn run_cli(fixture: &TempFixture, args: &[&str]) -> Output { + Command::new(env!("CARGO_BIN_EXE_wardoff")) + .args(args) + .env("LOCALAPPDATA", &fixture.root) + .output() + .expect("run Wardoff CLI") +} + +fn lines(contents: impl IntoIterator) -> String { + contents + .into_iter() + .map(|line| format!("{line}\r\n")) + .collect() +} + +#[test] +fn tail_above_limit_exits_one_without_panicking() { + let fixture = TempFixture::new("invalid"); + + for value in [usize::MAX.to_string(), "100001".to_string()] { + let output = run_cli(&fixture, &["--log", "--tail", &value]); + + assert_eq!(output.status.code(), Some(1)); + assert!(String::from_utf8_lossy(&output.stderr).contains("--tail")); + assert!(String::from_utf8_lossy(&output.stderr).contains("100000")); + assert!(!String::from_utf8_lossy(&output.stderr).contains("panicked")); + } +} + +#[test] +fn zero_tail_succeeds_with_empty_output() { + let fixture = TempFixture::new("zero"); + + let output = run_cli(&fixture, &["--log", "--tail", "0"]); + + assert_eq!(output.status.code(), Some(0)); + assert!(output.stdout.is_empty()); +} + +#[test] +fn tail_limit_and_default_tail_return_expected_records() { + let all_fixture = TempFixture::new("limit"); + all_fixture.write_log( + "wardoff.jsonl", + &lines((1..=3).map(|index| format!("record-{index}"))), + ); + + let all = run_cli(&all_fixture, &["--log", "--tail", "100000"]); + assert_eq!(all.status.code(), Some(0)); + assert_eq!(String::from_utf8_lossy(&all.stdout).lines().count(), 3); + + let default_fixture = TempFixture::new("default"); + default_fixture.write_log( + "wardoff.jsonl", + &lines((1..=25).map(|index| format!("record-{index}"))), + ); + let default = run_cli(&default_fixture, &["--log"]); + assert_eq!(default.status.code(), Some(0)); + let default_output = String::from_utf8_lossy(&default.stdout); + let default_lines: Vec<_> = default_output.lines().collect(); + assert_eq!(default_lines.len(), 20); + assert_eq!(default_lines[0], "record-6"); + assert_eq!(default_lines[19], "record-25"); +} + +#[test] +fn rotated_crlf_logs_are_returned_in_chronological_order() { + let fixture = TempFixture::new("rotation"); + fixture.write_log("wardoff.2.jsonl", &lines(["old-1".into(), "old-2".into()])); + fixture.write_log("wardoff.1.jsonl", &lines(["mid-1".into(), "mid-2".into()])); + fixture.write_log("wardoff.jsonl", &lines(["new-1".into(), "new-2".into()])); + + let output = run_cli(&fixture, &["--log", "--tail", "4"]); + + assert_eq!(output.status.code(), Some(0)); + let output_text = String::from_utf8_lossy(&output.stdout); + let output_lines: Vec<_> = output_text.lines().collect(); + assert_eq!(output_lines, ["mid-1", "mid-2", "new-1", "new-2"]); +} From 39aadd2d9f464305fe545e80d32328cfe9e4c0bf Mon Sep 17 00:00:00 2001 From: RomainROCH Date: Sun, 4 Oct 2026 21:09:12 +0200 Subject: [PATCH 08/15] chore: align CI checks and isolate smoke test cleanup --- - | 11 -- .github/PULL_REQUEST_TEMPLATE.md | 1 + .github/workflows/ci.yml | 17 ++- .github/workflows/release.yml | 20 +++- AGENTS.md | 14 ++- CHANGELOG.md | 6 +- CONTRIBUTING.md | 7 +- docs/DEVELOPMENT.md | 35 ++++-- docs/MVP_VALIDATION_MATRIX.md | 14 ++- tests/smoke_helpers.ps1 | 110 +++++++++++++++++ tests/smoke_safety.ps1 | 101 ++++++++++++++++ tests/smoke_test.ps1 | 198 +++++++++++++++++++++++-------- 12 files changed, 437 insertions(+), 97 deletions(-) delete mode 100644 - create mode 100644 tests/smoke_helpers.ps1 create mode 100644 tests/smoke_safety.ps1 diff --git a/- b/- deleted file mode 100644 index 150d57a..0000000 --- a/- +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 08f70a8..a11316a 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -6,6 +6,7 @@ - [ ] `scripts/check.ps1 -Mode Full` passes for Rust changes (or `-Mode Docs` for docs only) - [ ] `tests/developer_checks.ps1` passes if developer tooling changed +- [ ] `tests/smoke_safety.ps1` passes if smoke setup or cleanup changed - [ ] Relevant smoke/manual checks run in an isolated environment, or skips explained - [ ] Docs updated if behavior changed diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6c63998..492c9d8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,6 +31,7 @@ jobs: uses: dtolnay/rust-toolchain@stable with: toolchain: ${{ matrix.rust }} + components: rustfmt, clippy - name: Cache Cargo and target uses: actions/cache@v4 @@ -42,14 +43,17 @@ jobs: restore-keys: | ${{ runner.os }}-${{ matrix.rust }}-cargo- - - name: Cargo check - run: cargo check + - name: Full local checks + shell: pwsh + run: .\scripts\check.ps1 -Mode Full - - name: Cargo test - run: cargo test + - name: Developer tooling tests + shell: pwsh + run: .\tests\developer_checks.ps1 - - name: Cargo build --release - run: cargo build --release + - name: Smoke safety tests + shell: pwsh + run: .\tests\smoke_safety.ps1 - name: Smoke tests shell: pwsh @@ -61,3 +65,4 @@ jobs: name: wardoff-windows-release path: target/release/wardoff.exe if-no-files-found: error + retention-days: 7 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 296e3f4..9940ecd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -26,9 +26,23 @@ jobs: uses: dtolnay/rust-toolchain@stable with: toolchain: stable + components: rustfmt, clippy - - name: Build release binary - run: cargo build --release + - name: Full local checks + shell: pwsh + run: .\scripts\check.ps1 -Mode Full + + - name: Developer tooling tests + shell: pwsh + run: .\tests\developer_checks.ps1 + + - name: Smoke safety tests + shell: pwsh + run: .\tests\smoke_safety.ps1 + + - name: Smoke tests + shell: pwsh + run: .\tests\smoke_test.ps1 - name: Generate changelog id: changelog @@ -56,7 +70,7 @@ jobs: } $releaseBody = @( - "This is an unsigned binary built by CI. For a signed binary, see [TODO: Gumroad link]." + "This is an unsigned binary built and tested by CI. Official signed downloads are not available yet." "" $changelogHeader "" diff --git a/AGENTS.md b/AGENTS.md index 863d5ac..7aae43c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -36,12 +36,15 @@ powershell -NoProfile -ExecutionPolicy Bypass -File scripts/check.ps1 -Mode Docs Default `Fast` checks documentation, formatting, compilation and unit tests. `Full` also runs strict Clippy and builds the release binary. `Docs` only checks -local documentation links and encoding. The scripts stop on failure and never -launch Wardoff. Details and focused tests live in +local documentation links and encoding. The scripts stop on failure. Cargo tests +can launch read-only CLI commands with isolated logs; they never start the +protection runtime. Details and focused tests live in [DEVELOPMENT.md](docs/DEVELOPMENT.md#validation). -`tests/smoke_test.ps1` is a separate, state-changing integration suite: it stops -repo-built Wardoff processes, runs Block/Allow and can change scheduled tasks. +`tests/smoke_test.ps1` is a separate, state-changing integration suite: it runs +Block/Allow and can change scheduled tasks. It refuses an existing runtime in +the same session or a pre-existing Wardoff autostart task and only stops its own +test processes. Use an isolated Windows test environment; see the validation matrix. A successful build or unit test is not evidence of actual shutdown, suspend or tray behavior. @@ -62,6 +65,7 @@ build or unit test is not evidence of actual shutdown, suspend or tray behavior. Run focused checks while iterating and `Full` for Rust changes. For docs-only changes use `Docs`; for developer-tooling changes also run -`tests/developer_checks.ps1`. Record failures and skipped Windows/manual checks. +`tests/developer_checks.ps1` and `tests/smoke_safety.ps1`. Record failures and +skipped Windows/manual checks. Update the owning doc when its contract changes and keep links working. Do not duplicate this guide into harness-specific files. diff --git a/CHANGELOG.md b/CHANGELOG.md index 3fce0e5..3ac6317 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. ## [Unreleased] ### Changed +- Bounded `--log --tail N` to 100000 lines and made line-buffer reservation incremental and fallible, so extreme values return a readable error instead of a capacity-overflow panic. +- Ensure the runtime unregisters callbacks and shuts down its resources even when the Windows message loop fails; preserve both errors if cleanup also fails. +- Make the Windows smoke suite refuse existing runtime/autostart state and restrict cleanup to resources created by the test. +- Align CI and release validation with local documentation, formatting, strict Clippy, Rust tests and PowerShell fixtures, using the existing standard Windows jobs. - Replaced the sleep layer's periodic `SetThreadExecutionState` worker with owned system/display Power Requests and an identifiable Wardoff reason. Activation failures now reach the coordinator synchronously, and Allow/exit release the request object. - Kept the existing tray wake-restore flow and added only a request-pair renewal to the existing resume callback when the runtime remains in Block, based on the documented termination of requests at user-initiated sleep. No additional resume state machine, display notifications or polling was introduced. - Corrected sleep claims throughout maintained docs, CLI help and runtime logs: protection concerns idle sleep and automatic display timeout, subject to Windows policy. Explicit Sleep/Hibernate are not vetoed; Modern Standby on battery has additional limits. The original v0.1.0 hibernation claim below was too broad. @@ -19,7 +23,7 @@ All notable changes to this project will be documented in this file. - Hardened `\\.\pipe\WardoffControl` with explicit local-only security that grants the current user SID, SYSTEM, and Builtin Administrators access so the same interactive user can send `--block`/`--allow` across elevation boundaries, while remaining access-denied cases now surface a clear Wardoff message instead of raw `os error 5`. - Clarified in README, architecture notes, and agent guidance that Layer 3 is skipped normally on editions such as LTSC when `\Microsoft\Windows\UpdateOrchestrator\Reboot` is absent. - Aligned docs on the non-elevated read-only CLI contract, the `\\.\pipe\WardoffControl` and `\\.\pipe\WardoffStatus` split, default startup honesty, and current exit-code wording. -- Clarified in the docs that `wardoff --status` is quick when a runtime is active but can currently take roughly 4 seconds to return `{"state":"inactive"}` when no instance is running because the client exhausts sequential status-pipe and control-pipe retry loops. +- Clarified that `wardoff --status` returns inactive without pipe retries when the session mutex is free. Roughly four seconds of sequential pipe retries remain possible when the mutex is occupied but both IPC endpoints are unavailable. - Refreshed the documentation truth-source and read order so new contributors and agents can identify the current MVP status, documentation entrypoints, and likely next work from the repository alone. - Hardened Allow-mode deactivation so switching to Allow clears Block-mode behavior more cleanly. - Tightened autostart tray-sync honesty so elevated task changes are reflected accurately in the tray menu state. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c070f1c..9e28362 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -28,9 +28,10 @@ focused test filters, documentation checks and troubleshooting. ## Runtime validation is separate The [validation matrix](docs/MVP_VALIDATION_MATRIX.md) owns runtime acceptance. -The smoke suite stops repo-built Wardoff processes, starts Block/Allow runtimes, -appends logs, and can change Task Scheduler entries. Run it in an isolated -Windows test environment where those effects are acceptable: +The smoke suite starts Block/Allow runtimes, appends logs, and can change Task +Scheduler entries. It refuses a pre-existing runtime in the current session or +a Wardoff autostart task, and cleans up only its own processes. Run it in an +isolated Windows test environment where those effects are acceptable: ```powershell powershell -NoProfile -ExecutionPolicy Bypass -File tests/smoke_test.ps1 diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md index c401f34..ef61c09 100644 --- a/docs/DEVELOPMENT.md +++ b/docs/DEVELOPMENT.md @@ -44,9 +44,10 @@ crosses that boundary. Unit tests live in `#[cfg(test)]` modules beside the code | Session names and singleton handoff | [session_scope.rs](../src/session_scope.rs), [instance.rs](../src/instance.rs) | main `forward_request_to_primary`; handoff acceptance | | Tray menu, Explorer restart, icon | [tray/mod.rs](../src/tray/mod.rs), [tray/icon.rs](../src/tray/icon.rs) | main `handle_tray_action`; manual tray checks | | Autostart / trusted executable path | [autostart.rs](../src/autostart.rs) | runtime policy checkbox fallback; elevated smoke checks | -| JSONL events, rotation and tail | [logger/mod.rs](../src/logger/mod.rs) | main read-only log dispatch; smoke log checks | +| JSONL events, rotation and tail | [logger/mod.rs](../src/logger/mod.rs) | [log_tail_cli.rs](../tests/log_tail_cli.rs) exercises real CLI with isolated log fixtures | | Release manifest | [build.rs](../build.rs), [wardoff.manifest](../wardoff.manifest) | release build and smoke manifest check | | Developer checks and docs links | [check.ps1](../scripts/check.ps1), [check-docs.ps1](../scripts/check-docs.ps1) | [developer_checks.ps1](../tests/developer_checks.ps1) | +| Safe smoke setup and cleanup | [smoke_test.ps1](../tests/smoke_test.ps1) | [smoke_safety.ps1](../tests/smoke_safety.ps1); validation matrix | ## Runtime flow and invariants @@ -67,8 +68,9 @@ Effects remain in the existing runtime and subsystem owners. The two pipes have different contracts: status reads must remain available without elevation; control commands retain same-user/session access checks. -The inactive status path retries both pipes and may take about four seconds. -That known limitation is tracked in [PLAN.md](../PLAN.md). +The ordinary inactive status path checks the session mutex and returns without +pipe retries. If the mutex is occupied but neither pipe responds, the sequential +fallback can take about four seconds; that case is tracked in [PLAN.md](../PLAN.md). ## Validation @@ -78,13 +80,15 @@ That known limitation is tracked in [PLAN.md](../PLAN.md). | Rust change ready for review | `powershell -NoProfile -ExecutionPolicy Bypass -File scripts/check.ps1 -Mode Full` | Fast checks, strict Clippy, release build | | Documentation only | `powershell -NoProfile -ExecutionPolicy Bypass -File scripts/check.ps1 -Mode Docs` | Local Markdown destinations exist; valid UTF-8 without NUL bytes | | Developer scripts | `powershell -NoProfile -ExecutionPolicy Bypass -File tests/developer_checks.ps1` | Checker success/failure paths in temporary fixtures | +| Smoke safety helpers | `powershell -NoProfile -ExecutionPolicy Bypass -File tests/smoke_safety.ps1` | Guard and cleanup behavior with fake processes/tasks; no runtime launched | | Runtime integration | `powershell -NoProfile -ExecutionPolicy Bypass -File tests/smoke_test.ps1` | State-changing Windows integration; isolated environment | The check script resolves the repository from its own location, stops at the -first failed command and returns a nonzero exit code. It does not install tools, -launch the product or run the smoke suite. Unit tests include brief real Windows -Power Request acquisition/release and token checks; they do not launch the -long-lived runtime, edit scheduled tasks or cause a physical power transition. +first failed command and returns a nonzero exit code. It does not install tools +or run the smoke suite. Cargo tests include brief real Windows Power Request +acquisition/release and token checks, plus read-only CLI subprocesses with +isolated log fixtures. They do not launch the long-lived runtime, edit scheduled +tasks or cause a physical power transition. Individual Cargo commands, useful for focused diagnosis: @@ -106,6 +110,7 @@ cargo test --locked ipc::tests cargo test --locked instance::tests cargo test --locked session_scope::tests cargo test --locked autostart::tests +cargo test --locked --test log_tail_cli ``` The docs checker covers tracked and untracked, non-ignored Markdown. It checks @@ -113,9 +118,17 @@ local link destinations, not heading fragments, external websites or factual accuracy. Review those explicitly when changing them. Dated benchmark scripts remain separate from development checks. -Existing [CI](../.github/workflows/ci.yml) runs Cargo check/test, a release build -and smoke tests on Windows. The local Full check adds formatting, strict lint -and documentation checks; it does not claim to reproduce CI runtime coverage. +Both [CI](../.github/workflows/ci.yml) and the +[release workflow](../.github/workflows/release.yml) run the same Full check, +developer-tooling fixtures, smoke-safety fixtures and the real smoke suite on +Windows. Full itself remains safe for routine development and does not run the +state-changing smoke suite. A failed check prevents release publication. + +The workflows retain one standard Windows job per existing event, with no added +matrix or scheduled runs. Standard GitHub-hosted runners are +[free for public repositories](https://docs.github.com/en/actions/concepts/billing-and-usage). +CI binary artifacts expire after seven days. Reassess billing before making the +repository private or moving to a larger or paid runner. ## Common problems @@ -127,7 +140,7 @@ and documentation checks; it does not claim to reproduce CI runtime coverage. | Cargo waits for its build lock | Let the owning build finish; avoid concurrent builds sharing a target directory | | Formatting failure | Run `cargo fmt --all`, then inspect the diff | | `--locked` rejects dependency resolution | Check whether the task intentionally changed Cargo.toml; update and review the lockfile only for an intended dependency change | -| Smoke changes a running development session | Stop and use an isolated environment; its cleanup targets repo binaries and it writes real logs/tasks | +| Smoke refuses an existing runtime or autostart task | Use an isolated environment with neither present; do not remove someone else's state to make the test pass | | Unit tests pass but tray/power behavior is uncertain | Use the specific manual row in the validation matrix and report the environment | ## Keep the next change easy diff --git a/docs/MVP_VALIDATION_MATRIX.md b/docs/MVP_VALIDATION_MATRIX.md index 0c78e15..3984e84 100644 --- a/docs/MVP_VALIDATION_MATRIX.md +++ b/docs/MVP_VALIDATION_MATRIX.md @@ -3,9 +3,13 @@ Use this as the MVP acceptance checklist for Wardoff as it exists today. It keeps automated coverage, manual spot checks, admin-only validation, and disruptive VM-only checks separate. For everyday development, use the [local check commands](DEVELOPMENT.md#validation). -The smoke suite is state-changing: it stops repo-built Wardoff processes, starts -protection, writes real logs and may modify scheduled tasks. Use an isolated -Windows test environment even when not requesting a shutdown or suspend. +The smoke suite is state-changing: it starts protection, writes real logs and +may modify scheduled tasks. It refuses a runtime already present in the current +session or a pre-existing Wardoff autostart task, and only stops processes it +started. Use an isolated Windows test environment even when not requesting a +shutdown or suspend; preflight checks cannot prevent concurrent external changes. +Run [smoke_safety.ps1](../tests/smoke_safety.ps1) to check those guards with fakes +without changing the live session. ## Scope and honesty rules @@ -32,7 +36,7 @@ Keep validation language conservative: | Area | Notes | | --- | --- | | OS | Windows 10 or Windows 11 | -| Build target | `cargo build --release` then validate `target\release\wardoff.exe` | +| Build target | `cargo build --release --locked` then validate `target\release\wardoff.exe` | | Logs | `%LOCALAPPDATA%\Wardoff\logs\wardoff.jsonl` | | Admin rights | Required for Layer 3, Layer 4, Update Orchestrator task checks, and autostart task changes | | Trusted install path | Required if you expect `wardoff --autostart on` to create or update the `Wardoff` task; from an untrusted or user-writable path Wardoff should refuse safely and leave the scheduled-task state unchanged | @@ -42,7 +46,7 @@ Keep validation language conservative: | Check | What the script proves today | Admin needed | | --- | --- | --- | -| Release build | `cargo build --release` succeeds | No | +| Release build | `cargo build --release --locked` succeeds | No | | Release artifact | `target\release\wardoff.exe` exists | No | | CLI help | `wardoff --help` exits `0` and mentions `wardoff` | No | | CLI version | `wardoff --version` exits `0` and prints the package version | No | diff --git a/tests/smoke_helpers.ps1 b/tests/smoke_helpers.ps1 new file mode 100644 index 0000000..7ee0c9d --- /dev/null +++ b/tests/smoke_helpers.ps1 @@ -0,0 +1,110 @@ +Set-StrictMode -Version Latest + +function Get-SmokeCurrentSessionId { + return [System.Diagnostics.Process]::GetCurrentProcess().SessionId +} + +function Test-SmokeOwnedProcess { + param( + [Parameter(Mandatory = $true)] + [object] $ObservedProcess, + [Parameter(Mandatory = $true)] + [AllowEmptyCollection()] + [object[]] $OwnedProcesses + ) + + foreach ($ownedProcess in $OwnedProcesses) { + if ($null -eq $ownedProcess) { + continue + } + if ($ownedProcess.HasExited) { + continue + } + if ([int]$ObservedProcess.ProcessId -ne [int]$ownedProcess.Id) { + continue + } + # The live Process object owns a kernel handle; an exited object cannot + # authorize a reused PID to be treated as the smoke process. + return $true + } + return $false +} + +function Assert-SmokePreconditions { + param( + [Parameter(Mandatory = $true)] + [AllowEmptyCollection()] + [object[]] $WardoffProcesses, + [Parameter(Mandatory = $true)] + [int] $CurrentSessionId, + [Parameter(Mandatory = $true)] + [string] $BinaryPath, + [Parameter(Mandatory = $true)] + [scriptblock] $AutostartTaskProbe, + [AllowEmptyCollection()] + [object[]] $OwnedProcesses = @() + ) + + $resolvedBinaryPath = [System.IO.Path]::GetFullPath($BinaryPath) + $foreignProcess = @( + $WardoffProcesses | Where-Object { + (([int]$_.SessionId -eq $CurrentSessionId) -or + ($_.ExecutablePath -and + [string]::Equals( + [System.IO.Path]::GetFullPath([string]$_.ExecutablePath), + $resolvedBinaryPath, + [System.StringComparison]::OrdinalIgnoreCase + ))) -and + (-not (Test-SmokeOwnedProcess -ObservedProcess $_ -OwnedProcesses $OwnedProcesses)) + } + ) + if ($foreignProcess.Count -gt 0) { + $ids = ($foreignProcess | ForEach-Object { [int]$_.ProcessId }) -join ', ' + throw "Smoke test refused to start: a conflicting Wardoff runtime process already exists (session $CurrentSessionId or the same release binary in another session; PID(s): $ids). Use an isolated environment without an existing runtime." + } + + $taskExists = & $AutostartTaskProbe + if ([bool]$taskExists) { + throw 'Smoke test refused to start: the \Wardoff autostart task already exists. Use an isolated environment without an existing autostart task.' + } +} + +function Stop-SmokeOwnedProcesses { + param( + [Parameter(Mandatory = $true)] + [AllowEmptyCollection()] + [object[]] $OwnedProcesses, + [Parameter(Mandatory = $true)] + [scriptblock] $StopAction, + [scriptblock] $WaitAction = $null + ) + + foreach ($process in $OwnedProcesses) { + if ($null -eq $process) { + continue + } + try { + if (-not $process.HasExited) { + & $StopAction $process + if ($null -ne $WaitAction) { + & $WaitAction $process + } + } + } + catch { + if ($_.Exception.Message -notmatch 'cannot find|has exited') { + Write-Warning "Could not stop owned Wardoff process $($process.Id): $($_.Exception.Message)" + } + } + } +} + +function Test-SmokeAutostartTaskIdentity { + param( + [Parameter(Mandatory = $true)][object] $ExpectedIdentity, + [Parameter(Mandatory = $true)][object] $ActualIdentity + ) + return [string]::Equals($ExpectedIdentity.Xml, $ActualIdentity.Xml, [System.StringComparison]::Ordinal) -and + [string]::Equals($ExpectedIdentity.Path, $ActualIdentity.Path, [System.StringComparison]::OrdinalIgnoreCase) -and + [string]::Equals($ExpectedIdentity.WorkingDirectory, $ActualIdentity.WorkingDirectory, [System.StringComparison]::OrdinalIgnoreCase) +} diff --git a/tests/smoke_safety.ps1 b/tests/smoke_safety.ps1 new file mode 100644 index 0000000..9a48ff7 --- /dev/null +++ b/tests/smoke_safety.ps1 @@ -0,0 +1,101 @@ +[CmdletBinding()] +param() + +$ErrorActionPreference = 'Stop' +$helperPath = Join-Path $PSScriptRoot 'smoke_helpers.ps1' +. $helperPath + +function Assert-True { + param([bool]$Condition, [string]$Message) + if (-not $Condition) { throw $Message } +} + +function Assert-Throws { + param([scriptblock]$Action, [string]$MessageFragment) + $caught = $null + try { + & $Action + } + catch { + $caught = $_.Exception.Message + } + if ($null -eq $caught) { + throw "Expected an exception containing '$MessageFragment'." + } + if ($caught -notmatch [regex]::Escape($MessageFragment)) { + throw "Unexpected exception: $caught" + } +} + +try { + $binaryPath = Join-Path $env:TEMP 'Wardoff\target\release\wardoff.exe' + $canaryCaught = $false + try { Assert-Throws {} 'canary' } catch { $canaryCaught = $true } + Assert-True $canaryCaught 'Assert-Throws accepted an action that did not throw.' + + $probeState = @{ Calls = 0 } + $emptyProbe = { $probeState.Calls++; return $false } + Assert-SmokePreconditions -WardoffProcesses @() -CurrentSessionId 7 -BinaryPath $binaryPath -AutostartTaskProbe $emptyProbe + Assert-True ($probeState.Calls -eq 1) 'The autostart probe was not called for a clean fixture.' + + $sameSession = [pscustomobject]@{ ProcessId = 101; SessionId = 7; ExecutablePath = 'C:\other\wardoff.exe'; CreationDate = $null } + $probeState.Calls = 0 + Assert-Throws { + Assert-SmokePreconditions -WardoffProcesses @($sameSession) -CurrentSessionId 7 -BinaryPath $binaryPath -AutostartTaskProbe { $probeState.Calls++; return $false } + } 'conflicting Wardoff runtime process' + Assert-True ($probeState.Calls -eq 0) 'The task probe ran after a process guard failure.' + + $otherSession = [pscustomobject]@{ ProcessId = 102; SessionId = 8; ExecutablePath = 'C:\other\wardoff.exe'; CreationDate = $null } + Assert-SmokePreconditions -WardoffProcesses @($otherSession) -CurrentSessionId 7 -BinaryPath $binaryPath -AutostartTaskProbe { return $false } + + $sameBinaryOtherSession = [pscustomobject]@{ ProcessId = 103; SessionId = 8; ExecutablePath = $binaryPath; CreationDate = $null } + Assert-Throws { + Assert-SmokePreconditions -WardoffProcesses @($sameBinaryOtherSession) -CurrentSessionId 7 -BinaryPath $binaryPath -AutostartTaskProbe { return $false } + } 'conflicting Wardoff runtime process' + + $exitedOwned = [pscustomobject]@{ Id = 103; HasExited = $true; StartTime = [datetime]::UtcNow } + $observedReusedPid = [pscustomobject]@{ ProcessId = 103; SessionId = 7; ExecutablePath = $binaryPath; CreationDate = [datetime]::UtcNow } + Assert-Throws { + Assert-SmokePreconditions -WardoffProcesses @($observedReusedPid) -CurrentSessionId 7 -BinaryPath $binaryPath -AutostartTaskProbe { return $false } -OwnedProcesses @($exitedOwned) + } 'conflicting Wardoff runtime process' + + Assert-Throws { + Assert-SmokePreconditions -WardoffProcesses @() -CurrentSessionId 7 -BinaryPath $binaryPath -AutostartTaskProbe { return $true } + } 'autostart task already exists' + + Assert-Throws { + Assert-SmokePreconditions -WardoffProcesses @() -CurrentSessionId 7 -BinaryPath $binaryPath -AutostartTaskProbe { throw 'Task Scheduler query failed.' } + } 'Task Scheduler query failed' + + $taskIdentity = [pscustomobject]@{ Xml = ''; Path = $binaryPath; WorkingDirectory = (Split-Path -Parent $binaryPath) } + $sameTaskIdentity = [pscustomobject]@{ Xml = $taskIdentity.Xml; Path = $taskIdentity.Path; WorkingDirectory = $taskIdentity.WorkingDirectory } + $changedTaskIdentity = [pscustomobject]@{ Xml = ''; Path = $taskIdentity.Path; WorkingDirectory = $taskIdentity.WorkingDirectory } + Assert-True (Test-SmokeAutostartTaskIdentity -ExpectedIdentity $taskIdentity -ActualIdentity $sameTaskIdentity) 'An unchanged smoke-created task was not recognized as owned.' + Assert-True (-not (Test-SmokeAutostartTaskIdentity -ExpectedIdentity $taskIdentity -ActualIdentity $changedTaskIdentity)) 'A changed task definition was still recognized as owned.' + + $owned = [pscustomobject]@{ Id = 201; HasExited = $false } + $unowned = [pscustomobject]@{ Id = 202; HasExited = $false } + $stopped = [System.Collections.Generic.List[int]]::new() + Stop-SmokeOwnedProcesses -OwnedProcesses @($owned) -StopAction { + param($process) + $stopped.Add([int]$process.Id) + } -WaitAction { + param($process) + } + Assert-True ($stopped.Count -eq 1 -and $stopped[0] -eq 201) 'Cleanup did not stop exactly the owned process handle.' + Assert-True (-not ($stopped -contains $unowned.Id)) 'Cleanup attempted to stop an unowned process.' + + $syntaxErrors = $null + foreach ($path in @($helperPath, (Join-Path $PSScriptRoot 'smoke_test.ps1'))) { + [System.Management.Automation.Language.Parser]::ParseFile($path, [ref]$null, [ref]$syntaxErrors) | Out-Null + if ($syntaxErrors) { + throw (($syntaxErrors | ForEach-Object { $_.Message }) -join [Environment]::NewLine) + } + } + Write-Output 'Smoke safety tests passed.' + exit 0 +} +catch { + Write-Error $_.Exception.Message + exit 1 +} diff --git a/tests/smoke_test.ps1 b/tests/smoke_test.ps1 index a6c7cf3..bd4be44 100644 --- a/tests/smoke_test.ps1 +++ b/tests/smoke_test.ps1 @@ -2,9 +2,10 @@ .SYNOPSIS State-changing Windows integration tests; use an isolated test environment. .DESCRIPTION -Stops repo-built Wardoff processes (including a manually started development -instance), runs Block/Allow, writes real user logs and can change scheduled tasks -when elevated. For routine development use scripts/check.ps1 instead. +Refuses to start when a conflicting Wardoff runtime or autostart task already +exists, then runs Block/Allow using only processes launched by this test. It +writes real user logs and can change scheduled tasks when elevated. For routine +development use scripts/check.ps1 instead. See docs/MVP_VALIDATION_MATRIX.md for coverage and manual acceptance. #> [CmdletBinding()] @@ -14,9 +15,12 @@ Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' $script:Results = [System.Collections.Generic.List[object]]::new() +$script:OwnedProcesses = [System.Collections.Generic.List[object]]::new() $script:BackgroundProcess = $null $script:InitialLogLineCount = 0 +. (Join-Path $PSScriptRoot 'smoke_helpers.ps1') + $repoRoot = [System.IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..')) $logPath = Join-Path (Join-Path $env:LOCALAPPDATA 'Wardoff') 'logs\wardoff.jsonl' @@ -427,7 +431,7 @@ function Invoke-DirectPowerShellWardoffCommand { } function Get-WardoffProcessesByName { - $processes = Get-CimInstance Win32_Process -Filter "Name='wardoff.exe'" -ErrorAction SilentlyContinue + $processes = Get-CimInstance Win32_Process -Filter "Name='wardoff.exe'" -ErrorAction Stop if ($null -eq $processes) { return @() @@ -436,6 +440,107 @@ function Get-WardoffProcessesByName { return @($processes) } +function Get-SmokePreflightWardoffProcesses { + try { + return @(Get-WardoffProcessesByName) + } + catch { + throw "Smoke test could not query Wardoff processes before mutation: $($_.Exception.Message)" + } +} + +function Get-SmokeAutostartTaskExists { + $scheduler = $null + $rootFolder = $null + $task = $null + try { + $scheduler = New-Object -ComObject 'Schedule.Service' + $scheduler.Connect() + $rootFolder = $scheduler.GetFolder('\') + try { + $task = $rootFolder.GetTask('Wardoff') + return $true + } + catch { + $hresult = $_.Exception.GetBaseException().HResult + if ($hresult -in @(-2147024894, -2147024893, -2147023728)) { + return $false + } + throw + } + } + catch { + throw "Smoke test could not query the Wardoff autostart task before mutation: $($_.Exception.Message)" + } + finally { + foreach ($comObject in @($task, $rootFolder, $scheduler)) { + if ($null -ne $comObject) { + [void][Runtime.InteropServices.Marshal]::ReleaseComObject($comObject) + } + } + } +} + +function Get-SmokeAutostartTaskIdentity { + $scheduler = $null + $rootFolder = $null + $task = $null + $definition = $null + $actions = $null + $action = $null + try { + $scheduler = New-Object -ComObject 'Schedule.Service' + $scheduler.Connect() + $rootFolder = $scheduler.GetFolder('\') + $task = $rootFolder.GetTask('Wardoff') + $definition = $task.Definition + $actions = $definition.Actions + if ($actions.Count -ne 1) { + throw 'Wardoff autostart task has an unexpected number of actions.' + } + $action = $actions.Item(1) + $expectedPath = [System.IO.Path]::GetFullPath($binaryPath) + $actualPath = [System.IO.Path]::GetFullPath([string]$action.Path) + $expectedWorkingDirectory = [System.IO.Path]::GetFullPath((Split-Path -Parent $binaryPath)) + $actualWorkingDirectory = [System.IO.Path]::GetFullPath([string]$action.WorkingDirectory) + if (-not [string]::Equals($actualPath, $expectedPath, [System.StringComparison]::OrdinalIgnoreCase) -or + -not [string]::Equals($actualWorkingDirectory, $expectedWorkingDirectory, [System.StringComparison]::OrdinalIgnoreCase)) { + throw 'Refusing to claim an autostart task that does not target the smoke executable and its directory.' + } + return [pscustomobject]@{ + Xml = [string]$task.Xml + Path = $actualPath + WorkingDirectory = $actualWorkingDirectory + } + } + catch { + throw "Smoke test could not verify ownership of the Wardoff autostart task: $($_.Exception.Message)" + } + finally { + foreach ($comObject in @($action, $actions, $definition, $task, $rootFolder, $scheduler)) { + if ($null -ne $comObject) { + [void][Runtime.InteropServices.Marshal]::ReleaseComObject($comObject) + } + } + } +} + +function Test-SmokeOwnedAutostartTask { + param([Parameter(Mandatory = $true)][object]$ExpectedIdentity) + $actual = Get-SmokeAutostartTaskIdentity + return Test-SmokeAutostartTaskIdentity -ExpectedIdentity $ExpectedIdentity -ActualIdentity $actual +} + +function Assert-SmokePreflight { + $processes = Get-SmokePreflightWardoffProcesses + Assert-SmokePreconditions ` + -WardoffProcesses $processes ` + -CurrentSessionId (Get-SmokeCurrentSessionId) ` + -BinaryPath $binaryPath ` + -AutostartTaskProbe { Get-SmokeAutostartTaskExists } ` + -OwnedProcesses @($script:OwnedProcesses.ToArray()) +} + function Get-RepoWardoffProcesses { if (-not (Test-Path $binaryPath)) { return @() @@ -581,33 +686,15 @@ function Wait-ForBinaryUnlock { } function Stop-RepoWardoffProcesses { - $processes = @(Get-RepoWardoffProcesses) - - if ($processes.Count -gt 0 -and (Test-Path $binaryPath)) { - try { - $null = Invoke-ExternalCommand -FilePath $binaryPath -Arguments @('--allow') - Start-Sleep -Seconds 1 - } - catch { - } - } - - foreach ($process in $processes) { - try { - Stop-Process -Id $process.ProcessId -Force -ErrorAction Stop - } - catch { - if ($_.Exception.Message -notmatch 'cannot find') { - Write-Warning "Could not stop Wardoff process $($process.ProcessId): $($_.Exception.Message)" - } - } - } - - foreach ($process in $processes) { - try { - Wait-Process -Id $process.ProcessId -Timeout 5 -ErrorAction Stop - } - catch { + $processes = @($script:OwnedProcesses.ToArray()) + + Stop-SmokeOwnedProcesses -OwnedProcesses $processes -StopAction { + param($process) + Stop-Process -InputObject $process -Force -ErrorAction Stop + } -WaitAction { + param($process) + if (-not $process.WaitForExit(5000)) { + throw "Owned Wardoff process $($process.Id) did not exit within five seconds." } } @@ -740,6 +827,8 @@ function Resolve-PowercfgRequestsCheck { } } +Assert-SmokePreflight + try { Stop-RepoWardoffProcesses @@ -835,8 +924,10 @@ try { Invoke-TestCase 'Start-Process launches wardoff --block --hide in the background' { Stop-RepoWardoffProcesses + Assert-SmokePreflight $script:InitialLogLineCount = @(Get-StructuredLogLines).Count $script:BackgroundProcess = Start-Process -FilePath $binaryPath -ArgumentList @('--block', '--hide') -WorkingDirectory $repoRoot -WindowStyle Hidden -PassThru + $script:OwnedProcesses.Add($script:BackgroundProcess) Start-Sleep -Seconds 2 $script:BackgroundProcess.Refresh() Assert-Condition (-not $script:BackgroundProcess.HasExited) "Wardoff exited early with code $($script:BackgroundProcess.ExitCode)." @@ -960,38 +1051,38 @@ try { } else { Invoke-TestCase $autostartDescription { - $taskOriginallyPresent = (Invoke-ExternalCommand -FilePath 'schtasks' -Arguments @('/query', '/tn', 'Wardoff')).ExitCode -eq 0 - + Assert-SmokePreflight + $taskCreatedBySmoke = $false + $taskIdentity = $null try { $enableResult = Invoke-ExternalCommand -FilePath $binaryPath -Arguments @('--autostart', 'on') if ($enableResult.ExitCode -eq 0) { + $taskIdentity = Get-SmokeAutostartTaskIdentity + $taskCreatedBySmoke = $true $queryEnabled = Invoke-ExternalCommand -FilePath 'schtasks' -Arguments @('/query', '/tn', 'Wardoff') Assert-Condition ($queryEnabled.ExitCode -eq 0) 'schtasks /query /tn "Wardoff" did not find the task after --autostart on.' + Assert-Condition (Test-SmokeOwnedAutostartTask -ExpectedIdentity $taskIdentity) 'Refusing to remove the Wardoff task because its definition changed after creation.' $disableResult = Invoke-ExternalCommand -FilePath $binaryPath -Arguments @('--autostart', 'off') Assert-Condition ($disableResult.ExitCode -eq 0) "wardoff --autostart off exited with code $($disableResult.ExitCode)." - - $queryDisabled = Invoke-ExternalCommand -FilePath 'schtasks' -Arguments @('/query', '/tn', 'Wardoff') - Assert-Condition ($queryDisabled.ExitCode -ne 0) 'schtasks /query /tn "Wardoff" still found the task after --autostart off.' + Assert-Condition (-not (Get-SmokeAutostartTaskExists)) 'The Wardoff task still exists after --autostart off.' + $taskCreatedBySmoke = $false + $taskIdentity = $null } else { Assert-Condition ($enableResult.Output -match 'untrusted location') "wardoff --autostart on failed without the expected trusted-location refusal. Output: $($enableResult.Output)" - $queryAfterRefusal = Invoke-ExternalCommand -FilePath 'schtasks' -Arguments @('/query', '/tn', 'Wardoff') - Assert-Condition (($queryAfterRefusal.ExitCode -eq 0) -eq $taskOriginallyPresent) 'wardoff --autostart on changed the Wardoff task state even though it refused an untrusted executable location.' + Assert-Condition (-not (Get-SmokeAutostartTaskExists)) 'wardoff --autostart on changed the absent Wardoff task even though it refused an untrusted executable location.' } } finally { - try { - $taskPresentAfterTest = (Invoke-ExternalCommand -FilePath 'schtasks' -Arguments @('/query', '/tn', 'Wardoff')).ExitCode -eq 0 - if ($taskOriginallyPresent -and -not $taskPresentAfterTest) { - $null = Invoke-ExternalCommand -FilePath $binaryPath -Arguments @('--autostart', 'on') - } - elseif (-not $taskOriginallyPresent -and $taskPresentAfterTest) { - $null = Invoke-ExternalCommand -FilePath $binaryPath -Arguments @('--autostart', 'off') - } - } - catch { + if ($taskCreatedBySmoke -and (Get-SmokeAutostartTaskExists)) { + Assert-Condition (Test-SmokeOwnedAutostartTask -ExpectedIdentity $taskIdentity) 'Refusing to remove the Wardoff task because its definition changed after creation.' + $cleanupResult = Invoke-ExternalCommand -FilePath $binaryPath -Arguments @('--autostart', 'off') + Assert-Condition ($cleanupResult.ExitCode -eq 0) "wardoff --autostart off cleanup failed with exit code $($cleanupResult.ExitCode)." + Assert-Condition (-not (Get-SmokeAutostartTaskExists)) 'Smoke cleanup left the Wardoff task present.' + $taskCreatedBySmoke = $false + $taskIdentity = $null } } } @@ -1007,6 +1098,8 @@ try { } Invoke-TestCase 'Allow releases idle-power requests without stopping the runtime' { + Assert-SmokePreflight + Assert-Condition ($null -ne $script:BackgroundProcess -and -not $script:BackgroundProcess.HasExited) 'The smoke-owned runtime is no longer alive.' $repoProcesses = @(Get-RepoWardoffProcesses) Assert-Condition ($repoProcesses.Count -eq 1) 'Expected one background Wardoff process.' $allowResult = Invoke-ExternalCommand -FilePath $binaryPath -Arguments @('--allow') @@ -1034,15 +1127,16 @@ try { Invoke-TestCase 'Repeated Block reacquires idle-power requests before abrupt process termination' { foreach ($iteration in 1..2) { + Assert-SmokePreflight + Assert-Condition ($null -ne $script:BackgroundProcess -and -not $script:BackgroundProcess.HasExited) 'The smoke-owned runtime is no longer alive.' $blockResult = Invoke-ExternalCommand -FilePath $binaryPath -Arguments @('--block') Assert-Condition ($blockResult.ExitCode -eq 0) 'Block failed after Allow.' $activeStatus = Wait-ForActiveWardoffStatus Assert-Condition ($activeStatus.Status.layers.sleep -eq $true) 'Block did not reacquire idle-power requests.' } - # Deliberately do not call the normal cleanup helper: it sends --allow first. - $repoProcesses = @(Get-RepoWardoffProcesses) - Assert-Condition ($repoProcesses.Count -eq 1) 'Expected one owned runtime to terminate.' - $ownedProcess = Get-Process -Id $repoProcesses[0].ProcessId -ErrorAction Stop + # Deliberately terminate the owned runtime directly while it is blocked. + $ownedProcess = $script:BackgroundProcess + Assert-Condition ($null -ne $ownedProcess -and -not $ownedProcess.HasExited) 'Expected the smoke-owned runtime to remain alive before termination.' Stop-Process -InputObject $ownedProcess -Force -ErrorAction Stop Assert-Condition ($ownedProcess.WaitForExit(5000)) 'The terminated runtime did not exit within five seconds.' Assert-Condition (@(Get-RepoWardoffProcesses).Count -eq 0) 'Wardoff was still running after cleanup.' From 2320450c6a1304d8a82ae4645b6792defe50c796 Mon Sep 17 00:00:00 2001 From: RomainROCH Date: Mon, 5 Oct 2026 08:21:16 +0200 Subject: [PATCH 09/15] fix: preserve UTF-8 documentation paths on Windows --- scripts/check-docs.ps1 | 13 +++++++++++-- tests/developer_checks.ps1 | 6 ++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/scripts/check-docs.ps1 b/scripts/check-docs.ps1 index e0bbc8d..f613725 100644 --- a/scripts/check-docs.ps1 +++ b/scripts/check-docs.ps1 @@ -120,8 +120,17 @@ function Get-InlineTargets { } try { - $gitOutput = & git -C $resolvedRoot -c core.quotepath=false ls-files --cached --others --exclude-standard -- '*.md' 2>&1 - if ($LASTEXITCODE -ne 0) { + # Git emits unquoted paths as UTF-8. Windows PowerShell otherwise decodes + # them using the console code page, which corrupts non-ASCII filenames. + $previousConsoleEncoding = [Console]::OutputEncoding + try { + [Console]::OutputEncoding = $utf8Strict + $gitOutput = & git -C $resolvedRoot -c core.quotepath=false ls-files --cached --others --exclude-standard -- '*.md' 2>&1 + $gitExitCode = $LASTEXITCODE + } finally { + [Console]::OutputEncoding = $previousConsoleEncoding + } + if ($gitExitCode -ne 0) { throw "Unable to enumerate Markdown files with Git: $($gitOutput -join ' ')" } $paths = @($gitOutput | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | ForEach-Object { $_.ToString().Trim() } | Sort-Object -Unique) diff --git a/tests/developer_checks.ps1 b/tests/developer_checks.ps1 index 82b1531..a7eff92 100644 --- a/tests/developer_checks.ps1 +++ b/tests/developer_checks.ps1 @@ -75,10 +75,12 @@ try { '@ New-Item -ItemType Directory -Path (Join-Path $fixtureRoot 'docs') | Out-Null Set-Content -LiteralPath (Join-Path $fixtureRoot 'docs\guide file.md') -Encoding utf8 -Value '# Guide' - Set-Content -LiteralPath (Join-Path $fixtureRoot 'docs\nötes.md') -Encoding utf8 -Value '# Notes' + # Keep the fixture name independent of Windows PowerShell source decoding. + $unicodePath = 'docs/n' + [char]0x00F6 + 'tes.md' + Set-Content -LiteralPath (Join-Path $fixtureRoot $unicodePath) -Encoding utf8 -Value '# Notes' Set-Content -LiteralPath (Join-Path $fixtureRoot 'ignored.md') -Encoding utf8 -Value '[missing](nope.md)' Add-Content -LiteralPath (Join-Path $fixtureRoot '.gitignore') -Encoding utf8 -Value 'ignored.md' - git add README.md 'docs/guide file.md' 'docs/nötes.md' .gitignore + git add README.md 'docs/guide file.md' $unicodePath .gitignore $success = Invoke-FixtureChecker Assert-Contains $success 'Documentation checks passed' From dffd076fcf1dc340b943ee5d6a01b2af94b66e00 Mon Sep 17 00:00:00 2001 From: RomainROCH Date: Mon, 5 Oct 2026 08:23:23 +0200 Subject: [PATCH 10/15] ci: prepare verified release drafts from exact binaries --- .github/workflows/ci.yml | 15 +- .github/workflows/release.yml | 146 ++++++---- CHANGELOG.md | 9 + Cargo.lock | 2 +- Cargo.toml | 2 +- SECURITY.md | 2 +- docs/DEVELOPMENT.md | 10 +- docs/README.md | 1 + docs/RELEASE_NOTES_DRAFT.md | 85 +++--- docs/RELEASING.md | 113 ++++++++ scripts/release-candidate.ps1 | 110 ++++++++ scripts/release-guard.ps1 | 147 ++++++++++ tests/release_guards.ps1 | 510 ++++++++++++++++++++++++++++++++++ tests/smoke_helpers.ps1 | 94 +++++++ tests/smoke_safety.ps1 | 20 ++ tests/smoke_test.ps1 | 87 +----- wardoff.manifest | 2 +- 17 files changed, 1166 insertions(+), 189 deletions(-) create mode 100644 docs/RELEASING.md create mode 100644 scripts/release-candidate.ps1 create mode 100644 scripts/release-guard.ps1 create mode 100644 tests/release_guards.ps1 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 492c9d8..00c58dd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,6 +10,9 @@ on: - dev - main +permissions: + contents: read + env: CARGO_TARGET_DIR: target @@ -55,9 +58,19 @@ jobs: shell: pwsh run: .\tests\smoke_safety.ps1 + - name: Release guard tests + shell: pwsh + run: .\tests\release_guards.ps1 + - name: Smoke tests shell: pwsh - run: .\tests\smoke_test.ps1 + run: | + $before = (Get-FileHash target/release/wardoff.exe -Algorithm SHA256).Hash + .\tests\smoke_test.ps1 -UseExistingBinary + if ($LASTEXITCODE -ne 0) { throw 'Smoke failed.' } + if ((Get-FileHash target/release/wardoff.exe -Algorithm SHA256).Hash -ne $before) { + throw 'Smoke changed the checked binary.' + } - name: Upload release binary uses: actions/upload-artifact@v4 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9940ecd..b34cfab 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,31 +1,49 @@ -name: Release +name: Release draft on: push: tags: - - "v*" + - 'v*' permissions: - contents: write + contents: read + +concurrency: + group: release-${{ github.ref }} + cancel-in-progress: false env: CARGO_TARGET_DIR: target + RELEASE_TAG: ${{ github.ref_name }} jobs: - release: + validate: + if: github.repository == 'RomainROCH/Wardoff' && github.ref_type == 'tag' runs-on: windows-latest - + permissions: + contents: read + actions: read + outputs: + artifact-id: ${{ steps.artifact.outputs.artifact-id }} + binary-sha256: ${{ steps.binary.outputs.sha256 }} steps: - - name: Checkout + - name: Checkout exact candidate uses: actions/checkout@v4 with: + ref: ${{ github.sha }} fetch-depth: 0 - fetch-tags: true + persist-credentials: false + + - name: Validate tag, versions, main provenance and exact CI + shell: pwsh + env: + GH_TOKEN: ${{ github.token }} + run: .\scripts\release-candidate.ps1 -Mode Validate -ContextPath "$env:RUNNER_TEMP/release-context.json" - name: Install Rust uses: dtolnay/rust-toolchain@stable with: - toolchain: stable + toolchain: stable-x86_64-pc-windows-msvc components: rustfmt, clippy - name: Full local checks @@ -40,60 +58,80 @@ jobs: shell: pwsh run: .\tests\smoke_safety.ps1 - - name: Smoke tests + - name: Release guard tests shell: pwsh - run: .\tests\smoke_test.ps1 + run: .\tests\release_guards.ps1 - - name: Generate changelog - id: changelog + - name: Record the built binary hash + id: binary shell: pwsh - run: | - $currentTag = "${{ github.ref_name }}" - $previousTag = git describe --tags --abbrev=0 "$currentTag^" 2>$null - - if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($previousTag)) { - $previousTag = $null - } - - if ($previousTag) { - $commitRange = "$previousTag..$currentTag" - $changelogHeader = "## Changes since $previousTag" - } else { - $commitRange = $currentTag - $changelogHeader = "## Changes" - } - - $changelog = git log --oneline $commitRange - - if ([string]::IsNullOrWhiteSpace($changelog)) { - $changelog = "- No changes found." - } - - $releaseBody = @( - "This is an unsigned binary built and tested by CI. Official signed downloads are not available yet." - "" - $changelogHeader - "" - $changelog - ) -join [Environment]::NewLine - - "release_body<> $env:GITHUB_OUTPUT - $releaseBody >> $env:GITHUB_OUTPUT - "EOF" >> $env:GITHUB_OUTPUT - - - name: Create GitHub Release + run: '"sha256=$((Get-FileHash target/release/wardoff.exe -Algorithm SHA256).Hash)" >> $env:GITHUB_OUTPUT' + + - name: Smoke tests on the same binary + shell: pwsh + run: .\tests\smoke_test.ps1 -UseExistingBinary + + - name: Verify and package the exact checked binary shell: pwsh env: - GH_TOKEN: ${{ github.token }} - RELEASE_BODY: ${{ steps.changelog.outputs.release_body }} + CHECKED_SHA256: ${{ steps.binary.outputs.sha256 }} + run: .\scripts\release-candidate.ps1 -Mode Package -ContextPath "$env:RUNNER_TEMP/release-context.json" -PackageDirectory "$env:RUNNER_TEMP/release-package" -ExpectedSha256 $env:CHECKED_SHA256 + + - name: Save immutable candidate artifact + id: artifact + uses: actions/upload-artifact@v4 + with: + name: wardoff-release-candidate + path: ${{ runner.temp }}/release-package/* + if-no-files-found: error + retention-days: 7 + + draft: + needs: validate + runs-on: windows-latest + permissions: + contents: write + actions: read + steps: + - name: Checkout the same candidate + uses: actions/checkout@v4 + with: + ref: ${{ github.sha }} + fetch-depth: 0 + persist-credentials: false + + - name: Require an exact artifact ID and hash + shell: pwsh + env: + CHECKED_ARTIFACT_ID: ${{ needs.validate.outputs.artifact-id }} + CHECKED_SHA256: ${{ needs.validate.outputs.binary-sha256 }} run: | - gh release create "${{ github.ref_name }}" ` - --title "${{ github.ref_name }}" ` - --notes "$env:RELEASE_BODY" + . .\scripts\release-guard.ps1 + Assert-ReleaseTransferInputs -ArtifactId $env:CHECKED_ARTIFACT_ID -Sha256 $env:CHECKED_SHA256 + + - name: Download the exact artifact from validation + uses: actions/download-artifact@v4 + with: + artifact-ids: ${{ needs.validate.outputs.artifact-id }} + path: ${{ runner.temp }}/release-package + merge-multiple: true - - name: Upload release asset + - name: Recheck and create a draft only shell: pwsh env: GH_TOKEN: ${{ github.token }} + CHECKED_SHA256: ${{ needs.validate.outputs.binary-sha256 }} run: | - gh release upload "${{ github.ref_name }}" target/release/wardoff.exe --clobber + .\scripts\release-candidate.ps1 -Mode Validate -ContextPath "$env:RUNNER_TEMP/rechecked-context.json" + if ($LASTEXITCODE -ne 0) { throw 'Release preflight failed.' } + . .\scripts\release-guard.ps1 + $package = "$env:RUNNER_TEMP/release-package" + $null = Assert-ReleaseArtifact -PackageDirectory $package -CandidateCommit $env:GITHUB_SHA -TagName $env:RELEASE_TAG -ExpectedSha256 $env:CHECKED_SHA256 -ExpectedBuildRunId ([long]$env:GITHUB_RUN_ID) + # --verify-tag checks existence only. Resolve again, including annotated tags. + $refs = @(git ls-remote --exit-code "https://github.com/$env:GITHUB_REPOSITORY.git" "refs/tags/$env:RELEASE_TAG" "refs/tags/$env:RELEASE_TAG^{}") + if ($LASTEXITCODE -ne 0) { throw 'Cannot recheck the remote release tag.' } + if ((Resolve-ReleaseTagCommit -TagName $env:RELEASE_TAG -RemoteRefs $refs) -ne $env:GITHUB_SHA) { throw 'Remote tag moved after validation.' } + gh release create $env:RELEASE_TAG "$package/wardoff.exe" "$package/build-info.json" "$package/SHA256SUMS.txt" --repo $env:GITHUB_REPOSITORY --verify-tag --draft --title $env:RELEASE_TAG --notes-file docs/RELEASE_NOTES_DRAFT.md + if ($LASTEXITCODE -ne 0) { throw 'Draft creation failed; inspect any partial draft manually.' } + $created = gh release view $env:RELEASE_TAG --repo $env:GITHUB_REPOSITORY --json isDraft,tagName | ConvertFrom-Json + if ($LASTEXITCODE -ne 0 -or $created.isDraft -ne $true -or $created.tagName -cne $env:RELEASE_TAG) { throw 'The created release was not confirmed as a draft.' } diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ac6317..a0caeeb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,12 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +Target version: `0.2.1`. This is a local candidate, not a published release. + ### Changed +- Gate release tags on matching package/lock/manifest versions, main-branch provenance and successful Windows checks for the exact commit. Build and transfer the same checked binary into a draft only; publication remains manual. +- Bound newline-terminated control IPC requests to 4096 bytes and one total second, preserving independent status availability and rejection/recovery behavior. +- Validate autostart path access with identification tokens, and record disposable-VM idle-power acceptance without claiming physical hardware coverage. - Bounded `--log --tail N` to 100000 lines and made line-buffer reservation incremental and fallible, so extreme values return a readable error instead of a capacity-overflow panic. - Ensure the runtime unregisters callbacks and shuts down its resources even when the Windows message loop fails; preserve both errors if cleanup also fails. - Make the Windows smoke suite refuse existing runtime/autostart state and restrict cleanup to resources created by the test. @@ -12,6 +17,10 @@ All notable changes to this project will be documented in this file. - Replaced the sleep layer's periodic `SetThreadExecutionState` worker with owned system/display Power Requests and an identifiable Wardoff reason. Activation failures now reach the coordinator synchronously, and Allow/exit release the request object. - Kept the existing tray wake-restore flow and added only a request-pair renewal to the existing resume callback when the runtime remains in Block, based on the documented termination of requests at user-initiated sleep. No additional resume state machine, display notifications or polling was introduced. - Corrected sleep claims throughout maintained docs, CLI help and runtime logs: protection concerns idle sleep and automatic display timeout, subject to Windows policy. Explicit Sleep/Hibernate are not vetoed; Modern Standby on battery has additional limits. The original v0.1.0 hibernation claim below was too broad. + +## [0.2.0] - 2026-05-03 + +### Changed - Hardened the session-scoped `WardoffControl` and `WardoffStatus` pipes against same-session squatting by claiming first pipe instances with bounded startup retries, failing closed until both IPC servers own their initial instances, and probing the singleton before `wardoff --status` opens IPC endpoints. - Scoped the singleton mutex plus the `WardoffControl` and `WardoffStatus` named pipes to the current Windows session so cross-session mutex or pipe squatting no longer breaks Wardoff's single-instance model across all sessions. - Hardened structured JSONL logging under `%LOCALAPPDATA%\Wardoff\logs` so Wardoff now creates its managed log directories one component at a time, refuses reparse points in the managed log tree and rotated files, and uses reparse-aware no-follow opens for active and read-only log file access. diff --git a/Cargo.lock b/Cargo.lock index 17d0228..62933ef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1446,7 +1446,7 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "wardoff" -version = "0.2.0" +version = "0.2.1" dependencies = [ "chrono", "clap", diff --git a/Cargo.toml b/Cargo.toml index 38f6d15..44984c0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wardoff" -version = "0.2.0" +version = "0.2.1" edition = "2021" authors = ["Romain ROCH"] description = "The first modern open-source tool for taking control of Windows shutdowns." diff --git a/SECURITY.md b/SECURITY.md index 1bcdccf..14c1710 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -7,7 +7,7 @@ Wardoff is a Windows-native utility that intentionally runs elevated for some fe | Version | Status | | --- | --- | | `dev` branch | Supported | -| `v0.1.0-mvp` (latest tagged release) | Supported | +| `v0.2.0` (latest published release) | Supported | Older versions are not supported for security reporting. diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md index ef61c09..5911880 100644 --- a/docs/DEVELOPMENT.md +++ b/docs/DEVELOPMENT.md @@ -121,11 +121,13 @@ remain separate from development checks. Both [CI](../.github/workflows/ci.yml) and the [release workflow](../.github/workflows/release.yml) run the same Full check, developer-tooling fixtures, smoke-safety fixtures and the real smoke suite on -Windows. Full itself remains safe for routine development and does not run the -state-changing smoke suite. A failed check prevents release publication. +Windows. Full includes the brief native Power Request test and does not run the +state-changing smoke suite. A failed required check prevents draft creation. -The workflows retain one standard Windows job per existing event, with no added -matrix or scheduled runs. Standard GitHub-hosted runners are +CI retains one standard Windows job; tag releases separate read-only validation +from a draft-only writer, with no added matrix or scheduled runs. The checked +binary is reused by smoke without compilation. See [RELEASING.md](RELEASING.md) +for provenance, exact-artifact gates and manual publication. Standard GitHub-hosted runners are [free for public repositories](https://docs.github.com/en/actions/concepts/billing-and-usage). CI binary artifacts expire after seven days. Reassess billing before making the repository private or moving to a larger or paid runner. diff --git a/docs/README.md b/docs/README.md index 06f5760..b76de45 100644 --- a/docs/README.md +++ b/docs/README.md @@ -25,6 +25,7 @@ small change. | Reporting security issues | [Security policy](../SECURITY.md) | | Questions and support | [Support](../SUPPORT.md) | | Released changes | [Changelog](../CHANGELOG.md) | +| Release validation, drafts and manual publication | [Releasing](RELEASING.md) | ## Evidence, comparisons and drafts diff --git a/docs/RELEASE_NOTES_DRAFT.md b/docs/RELEASE_NOTES_DRAFT.md index 46e9ee3..f59c89d 100644 --- a/docs/RELEASE_NOTES_DRAFT.md +++ b/docs/RELEASE_NOTES_DRAFT.md @@ -1,44 +1,41 @@ -# Wardoff v0.2.0 (draft, unreleased) - -> Draft only — this version has not shipped yet. - -## What's new since v0.1.0-mvp - -### Fixed -- Fixed non-elevated CLI launch paths so read-only commands behave like normal PowerShell and console commands, with more honest exit codes and status output. -- Fixed status and control IPC edge cases across elevation boundaries, including same-user control-pipe access, mutex access-denied handling, and inactive/handoff reporting. -- Fixed tray/runtime rough edges around delayed startup, secondary handoff recovery, wake-capable tray actions, allow-mode teardown, and recovery-failure reporting. -- Fixed autostart tray sync wording so admin-only behavior is reported honestly. -- Fixed Block mode not restoring after tray-initiated sleep/hibernate wake. -- Resolved the five original pre-release security blockers: writable-path autostart, log-directory symlink abuse, named-pipe squatting, cross-session mutex exposure, and WM_ENDSESSION spoofing. Same-session mutex hardening remains local follow-up work. - -### Improved -- Added best-effort internal Layer 2 local `shutdown.exe` detection and abort handling in Block mode, while keeping it out of headline MVP claims and documenting the limits conservatively. -- Improved Windows launch UX so long-lived runtime launches detach more cleanly from shell sessions and background startup behaves more predictably. -- Hardened the release manifest and non-elevating read-only CLI dispatch to reduce privilege surprises and keep read-only flows safer. -- Tightened smoke and validation coverage for read-only CLI paths and process cleanup timing. -- Added internal security audit (docs/SECURITY_AUDIT.md). -- Console window no longer flashes on startup. - -### Documentation -- Added GitHub issue forms for bug reports, compatibility reports, documentation issues, and feature requests. -- Added `docs/MVP_VALIDATION_MATRIX.md` and expanded manual validation guidance for tray behavior, Explorer restart recovery, handoff cases, and admin-only paths. -- Refreshed README, contributing guidance, shutdown-layer docs, CLI exit-code docs, and repo guidance to better reflect the current conservative MVP surface. -- Cleaned up repo documentation links and removed the old session-tracker status file. - -## Known limits -- Layer 3 (UpdateOrchestrator) is automatically skipped on LTSC editions that lack the `UpdateOrchestrator\Reboot` scheduled task. This is normal. -- Idle-power requests support S3 and Modern Standby, subject to Windows power policy. Explicit Sleep/Hibernate, lid and power-button actions are not vetoed. Hardware sleep behavior must be tested on a capable machine; VM request visibility alone is not proof. -- `shutdown /t 0 /f` is not guaranteed blockable. This is a Windows limitation, not a Wardoff bug. -- The product is source-first: no installer, no package manager distribution yet - -## How to test -- Clone the repo and run `cargo build --release` -- Binary: `target\release\wardoff.exe` -- Requires Windows 10 or later -- Some features require admin rights (layers 2-4, autostart) - -## How to report issues -- Bugs: use the bug report issue form -- Compatibility: use the compatibility report form -- Questions and feedback: use GitHub Discussions +# Wardoff v0.2.1 (draft, unreleased) + +This candidate has not been published. The tag workflow prepares a draft only +after validation; a maintainer must review and publish it separately. + +## Changes since v0.2.0 + +- Bound each newline-terminated control request to 4096 wire bytes and one total + second. Malformed, oversized, incomplete and disconnected clients cannot hold + the control reader indefinitely; status remains independently available. +- Use process-owned system/display Power Requests for idle-power protection. + Report activation errors synchronously, release on Allow/exit, and renew after + the existing resume notification. Explicit Sleep/Hibernate are not vetoed. +- Fix autostart access checks to use identification tokens. Keep the existing + trusted-path and same-user/elevation boundaries. +- Reject excessive log-tail requests with a readable error and bound allocation. + Clean up runtime resources when the Windows message loop fails. +- Isolate Windows smoke cleanup, consolidate developer instructions and checks, + and validate release provenance, versions and the exact binary before creating + a draft. No automatic public release or signing is added. + +## Validation and limits + +The draft includes `build-info.json` and `SHA256SUMS.txt` for its exact unsigned +MSVC binary and the Windows workflow run. Review the run and its explicit smoke +skips before publishing. A green run on another commit is not release evidence. + +- Disposable-VM idle-power results are recorded in + [IDLE_POWER_VALIDATION.md](IDLE_POWER_VALIDATION.md); physical S3/monitor, + Hibernate and Modern Standby acceptance remains incomplete. +- Missing UpdateOrchestrator Reboot tasks and unavailable interactive linked + tokens cause documented smoke skips, not successful coverage of those paths. +- Same-session mutex denial of service, the status pipe DACL and other residual + local hardening debt remain documented in [SECURITY_AUDIT.md](SECURITY_AUDIT.md). +- Forced zero-second shutdown and explicit power actions are not guaranteed + blockable. This remains a source-first utility for self-administered machines. +- Official signed downloads and paid support are not available. The source + remains MIT and support remains community-only. + +The resource benchmark retained in the repository covers its dated v0.2.0 +artifact; it has not been rerun or requalified for this candidate. diff --git a/docs/RELEASING.md b/docs/RELEASING.md new file mode 100644 index 0000000..7b24328 --- /dev/null +++ b/docs/RELEASING.md @@ -0,0 +1,113 @@ +# Releasing Wardoff + +The supported flow is **tag -> Windows validation -> exact binary -> draft -> +manual publication**. A tag does not authorize an automatic public release. +The current `0.2.1` source version is provisional; no matching release is shipped. + +## Why drafts rather than automatic publication + +Automatic builds and draft preparation remove repetitive packaging work. Keep +publication manual for this small source-first project: binaries are unsigned, +some interactive UAC cases are skipped on hosted runners, and physical S3, +Hibernate and Modern Standby acceptance is incomplete. A maintainer must review +the actual evidence and wording before making a binary available publicly. + +Fully manual packaging would avoid automation but make source/asset mismatches +easier. Fully automatic public releases would give a green job more authority +than the evidence supports. The draft flow preserves automation's useful part +without adding certificates, services, secrets, paid runners or commercial claims. +See [BUSINESS_MODEL.md](BUSINESS_MODEL.md) and [SUPPORT.md](../SUPPORT.md). + +## Before requesting a tag + +1. Integrate the reviewed candidate through `dev` into `main`. Keep other agents' + worktrees and unique branches intact. +2. Choose a stable `vX.Y.Z` with no leading zeroes, prerelease or build suffix. + Match `[package]` in Cargo.toml, the `wardoff` entry in Cargo.lock and manifest + assembly version `X.Y.Z.0`; keep `asInvoker` and `uiAccess=false`. +3. Update [CHANGELOG.md](../CHANGELOG.md) and + [RELEASE_NOTES_DRAFT.md](RELEASE_NOTES_DRAFT.md) from the previous published tag. + Keep dated benchmark and VM evidence bound to their original binary. +4. Wait for the exact commit's latest **push/main** CI run and its Windows steps + to succeed. A PR run, another branch/SHA, a newer failed rerun, missing evidence + or a skipped required step cannot satisfy the gate. Do not filter failed runs + away or add `continue-on-error` to make publication proceed. +5. Obtain authorization for the actual integration/push/tag and its draft effect. + Local preparation and local success do not authorize those external actions. + +The tag's commit must be reachable from current remote `main`; it need not be +the tip. The remote tag must still peel to the checked-out commit, including an +annotated tag. Git/API errors fail closed. If CI is pending, rerun the tag workflow +only after it succeeds; there is no bypass or polling service. + +## What the workflow does + +[release.yml](../.github/workflows/release.yml) uses two standard Windows jobs. +The validation job has only `contents: read` and `actions: read`. It validates +provenance and versions, runs Full checks, developer fixtures, smoke safety and +release guards, then the state-changing Windows smoke in the disposable hosted +runner. Native Power Request tests are not excluded there. Full builds with +`--locked`; smoke uses `-UseExistingBinary` and cannot compile a replacement. + +The hash is taken before smoke and must match afterward. Package checks verify +help/version, the real embedded manifest resource, Windows file version and MSVC +host. They copy that binary without rebuilding it and produce: + +- `wardoff.exe`: the exact unsigned checked binary; +- `SHA256SUMS.txt`: its SHA-256; +- `build-info.json`: tag, full source SHA, main CI run/attempt, build run URL, + Rust version/target, hash, unsigned status, tests and known limits. + +The draft job depends on successful validation. Only this job receives +`contents: write`, with `actions: read` for evidence. It downloads the immutable +artifact ID emitted by validation, verifies the binary hash and metadata, and +rechecks provenance and CI. It then resolves the remote tag again immediately +before `gh release create --verify-tag --draft`. `--verify-tag` alone proves only +existence, as documented in the [GitHub CLI manual](https://cli.github.com/manual/gh_release_create). + +Any existing release **or draft** for the tag blocks creation. There is no asset +clobber, automatic draft edit or publication command. After a failed upload, +inspect a partial draft manually; rerunning will refuse to overwrite it. The +write-job check matters because read-only API responses can omit drafts. + +## Review before manual publication + +Verify the draft tag still identifies the recorded SHA, download the actual +assets and check their hash, inspect the exact run's tests and explicit smoke +skips, and ensure the release notes match its scope. Keep hardware/UAC and local +security debt visible. Successful VM/request checks do not establish universal +power behavior; see [IDLE_POWER_VALIDATION.md](IDLE_POWER_VALIDATION.md), +[MVP_VALIDATION_MATRIX.md](MVP_VALIDATION_MATRIX.md) and +[SECURITY_AUDIT.md](SECURITY_AUDIT.md). Publication requires separate authorization. + +## Limits of repository-only guards + +These guards apply to the workflow committed at the new tag. An older tag can +carry an older workflow, and a maintainer able to change workflows can remove +checks. Repository scripts are not a security boundary against that maintainer. +External tag rules/permissions could address that separately, but no account or +branch/tag protection settings are changed by this work. + +There is a small race between the final remote-tag read and draft creation; +manual review must check the tag again before publication. Drafts remain editable +on GitHub. Neither SHA-256 nor these checks is an Authenticode signature or an +attestation service. Do not add a PAT or broader permission to bypass a denied +release creation; inspect the integration and GitHub error instead. + +## Local verification without system effects + +```powershell +powershell -NoProfile -ExecutionPolicy Bypass -File scripts/check.ps1 -Mode Docs +powershell -NoProfile -ExecutionPolicy Bypass -File tests/developer_checks.ps1 +powershell -NoProfile -ExecutionPolicy Bypass -File tests/smoke_safety.ps1 +powershell -NoProfile -ExecutionPolicy Bypass -File tests/release_guards.ps1 +``` + +These fixture suites use fake metadata/processes and owned temporary files, not +network mutation or the protection runtime. `Fast`/`Full` also execute a real +Power Request test; the full smoke starts Block/Allow and can edit tasks. When +those effects are not authorized locally, run individual Cargo checks and +explicitly exclude only +`blocker::sleep::tests::activation_reports_an_acquired_request_before_returning`. +Record that exclusion as NOT RUN. It does not replace the complete hosted checks +required for a draft. Local fixture success is not hosted workflow acceptance. diff --git a/scripts/release-candidate.ps1 b/scripts/release-candidate.ps1 new file mode 100644 index 0000000..5476e06 --- /dev/null +++ b/scripts/release-candidate.ps1 @@ -0,0 +1,110 @@ +<# +.SYNOPSIS +Validates release provenance or packages a previously checked Windows binary. +.DESCRIPTION +Validate performs only Git/GitHub reads. Package never compiles or starts the +protection runtime. This script never creates a tag, release or asset remotely. +#> +[CmdletBinding()] +param( + [ValidateSet('Validate', 'Package')][string] $Mode = 'Validate', + [string] $TagName = $env:RELEASE_TAG, + [string] $Repository = $env:GITHUB_REPOSITORY, + [string] $CandidateCommit = $env:GITHUB_SHA, + [string] $ContextPath, + [string] $PackageDirectory, + [string] $ExpectedSha256 +) +$ErrorActionPreference = 'Stop' +Set-StrictMode -Version Latest +$repoRoot = (Resolve-Path -LiteralPath (Join-Path $PSScriptRoot '..')).Path +. (Join-Path $PSScriptRoot 'release-guard.ps1') + +function Invoke-ReleaseGit { + param([string[]] $GitArgs) + $result = @(& git -C $repoRoot @GitArgs) + if ($LASTEXITCODE -ne 0) { throw 'Release Git read failed.' } + return $result +} + +function Read-ReleaseApiPages { + param([string] $Endpoint) + $json = & gh api --method GET --paginate --slurp $Endpoint + if ($LASTEXITCODE -ne 0) { throw 'Release GitHub read failed; no fallback is allowed.' } + return ($json | ConvertFrom-Json) +} + +$head = @(Invoke-ReleaseGit @('rev-parse', 'HEAD'))[0] +if ($CandidateCommit -notmatch '^[0-9a-fA-F]{40}$' -or $head -ne $CandidateCommit) { + throw 'Checkout is not the requested candidate commit.' +} +if (@(Invoke-ReleaseGit @('status', '--porcelain=v1')).Count -ne 0) { throw 'Release checkout is dirty.' } +if ([string]::IsNullOrWhiteSpace($ContextPath)) { throw 'A release context path is required.' } + +if ($Mode -eq 'Validate') { + # Validate names/versions before interpolating them in a Git or API argument. + $null = Get-ReleaseVersion -RepoRoot $repoRoot -TagName $TagName + if ($Repository -cne 'RomainROCH/Wardoff') { throw 'Release repository is not authorized.' } + $remote = 'https://github.com/' + $Repository + '.git' + $null = Invoke-ReleaseGit @('fetch', '--no-tags', $remote, '+refs/heads/main:refs/remotes/release/main') + & git -C $repoRoot merge-base --is-ancestor $head refs/remotes/release/main + $ancestryExit = $LASTEXITCODE + if ($ancestryExit -notin @(0, 1)) { throw 'Main ancestry is indeterminate.' } + $remoteRefs = Invoke-ReleaseGit @('ls-remote', '--exit-code', $remote, ('refs/tags/' + $TagName), ('refs/tags/' + $TagName + '^{}')) + $tagCommit = Resolve-ReleaseTagCommit -TagName $TagName -RemoteRefs $remoteRefs + $runsEndpoint = "repos/$Repository/actions/workflows/ci.yml/runs?branch=main&event=push&head_sha=$head&per_page=100" + $runs = @(foreach ($page in @(Read-ReleaseApiPages $runsEndpoint)) { $page.workflow_runs }) + $ci = Select-ReleaseCiRun -CiRuns $runs -Repository $Repository -CandidateCommit $head + $jobs = @(foreach ($page in @(Read-ReleaseApiPages "repos/$Repository/actions/runs/$($ci.id)/attempts/$($ci.run_attempt)/jobs?per_page=100")) { $page.jobs }) + $releases = @(foreach ($page in @(Read-ReleaseApiPages "repos/$Repository/releases?per_page=100")) { $page }) + $runs = @(foreach ($page in @(Read-ReleaseApiPages $runsEndpoint)) { $page.workflow_runs }) + $recheckedCi = Select-ReleaseCiRun -CiRuns $runs -Repository $Repository -CandidateCommit $head + if ($recheckedCi.id -ne $ci.id -or $recheckedCi.run_attempt -ne $ci.run_attempt -or $recheckedCi.updated_at -cne $ci.updated_at) { + throw 'CI evidence changed during release validation; retry only after it settles.' + } + $context = Assert-ReleaseCandidate -RepoRoot $repoRoot -Repository $Repository -TagName $TagName -CandidateCommit $head -RemoteTagCommit $tagCommit -MainContainsCandidate ($ancestryExit -eq 0) -CiRuns $runs -CiJobs $jobs -ExistingReleases $releases + $context | ConvertTo-Json | Set-Content -LiteralPath $ContextPath -Encoding utf8 + Write-Output "PASS: $TagName at $head; main CI run $($context.CiRunId)." + exit 0 +} + +$context = Get-Content -LiteralPath $ContextPath -Raw | ConvertFrom-Json +$version = Get-ReleaseVersion -RepoRoot $repoRoot -TagName $context.Tag +if ($context.Commit -ne $head -or $context.Tag -cne $TagName -or $context.Version -cne $version -or + -not (Test-ReleasePositiveInteger $context.CiRunId)) { throw 'Release context differs from the checkout.' } +if ($env:GITHUB_RUN_ID -notmatch '^[1-9][0-9]*$') { throw 'Build run ID is missing.' } +if ($ExpectedSha256 -notmatch '^[0-9a-fA-F]{64}$') { throw 'The pre-smoke binary hash is required.' } +. (Join-Path $repoRoot 'tests/smoke_helpers.ps1') +$binary = Join-Path $repoRoot 'target/release/wardoff.exe' +$hash = (Get-FileHash -LiteralPath $binary -Algorithm SHA256).Hash +if ($hash -ne $ExpectedSha256) { throw 'Smoke changed the release binary.' } +$versionOutput = @(& $binary --version) +if ($LASTEXITCODE -ne 0 -or ($versionOutput -join "`n").Trim() -cne "wardoff $version") { throw 'Release executable version differs.' } +$helpOutput = @(& $binary --help) +if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace(($helpOutput -join "`n"))) { throw 'Release help failed.' } +[xml]$manifest = Get-EmbeddedManifestContent -Path $binary +$identity = $manifest.SelectNodes("/*[local-name()='assembly']/*[local-name()='assemblyIdentity']") +$level = $manifest.SelectNodes("//*[local-name()='requestedExecutionLevel']") +if ($identity.Count -ne 1 -or $identity[0].GetAttribute('version') -cne ($version + '.0') -or + $level.Count -ne 1 -or $level[0].GetAttribute('level') -cne 'asInvoker' -or $level[0].GetAttribute('uiAccess') -cne 'false') { + throw 'Embedded release manifest differs from the source contract.' +} +$fileVersion = (Get-Item -LiteralPath $binary).VersionInfo.FileVersion +if ($fileVersion -cne ($version + '.0')) { throw 'Windows file version differs from the source contract.' } +$rustVersion = (& rustc -Vv) -join "`n" +if ($LASTEXITCODE -ne 0 -or $rustVersion -notmatch 'host: x86_64-pc-windows-msvc') { throw 'Release must use the MSVC host toolchain.' } +if (Test-Path -LiteralPath $PackageDirectory) { throw 'Package destination already exists.' } +$null = New-Item -ItemType Directory -Path $PackageDirectory +Copy-Item -LiteralPath $binary -Destination (Join-Path $PackageDirectory 'wardoff.exe') +$info = [ordered]@{ + tag = $TagName; source_commit = $head; version = $version; target = 'x86_64-pc-windows-msvc' + rustc = $rustVersion; unsigned = $true; sha256 = $hash; build_run_id = [long]$env:GITHUB_RUN_ID + run_url = "https://github.com/$Repository/actions/runs/$env:GITHUB_RUN_ID" + ci_run_id = $context.CiRunId; ci_attempt = $context.CiAttempt + tests = @('Full local checks', 'Developer tooling tests', 'Smoke safety tests', 'Release guard tests', 'Smoke tests with the same binary') + limits = @('Review explicit smoke skips in the run log.', 'Physical S3/monitor, Hibernate and Modern Standby acceptance remains incomplete.', 'Unsigned; community support only.') +} +$info | ConvertTo-Json -Depth 5 | Set-Content -LiteralPath (Join-Path $PackageDirectory 'build-info.json') -Encoding utf8 +"$hash wardoff.exe" | Set-Content -LiteralPath (Join-Path $PackageDirectory 'SHA256SUMS.txt') -Encoding ascii +$null = Assert-ReleaseArtifact -PackageDirectory $PackageDirectory -CandidateCommit $head -TagName $TagName -ExpectedSha256 $ExpectedSha256 -ExpectedBuildRunId ([long]$env:GITHUB_RUN_ID) +Write-Output "PASS: exact unsigned MSVC package, SHA256 $hash." diff --git a/scripts/release-guard.ps1 b/scripts/release-guard.ps1 new file mode 100644 index 0000000..a2382f7 --- /dev/null +++ b/scripts/release-guard.ps1 @@ -0,0 +1,147 @@ +# Pure release policy. Network reads and binary execution belong to the driver. +Set-StrictMode -Version Latest + +function Test-ReleasePositiveInteger { + param($Value) + return (($Value -is [int] -or $Value -is [long] -or $Value -is [uint32] -or $Value -is [uint64]) -and $Value -gt 0) +} + +function Get-ReleaseVersion { + param([string] $RepoRoot, [string] $TagName) + if ($TagName -cnotmatch '^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$') { + throw 'Release tag must be a stable vX.Y.Z without leading zeroes.' + } + $version = $TagName.Substring(1) + foreach ($part in $version.Split('.')) { + if ([decimal]$part -gt 65535) { throw 'Release version exceeds the Windows version range.' } + } + $toml = Get-Content -LiteralPath (Join-Path $RepoRoot 'Cargo.toml') -Raw + $package = [regex]::Matches($toml, '(?ms)^\[package\]\s*\r?\n(.*?)(?=^\[|\z)') + if ($package.Count -ne 1 -or $package[0].Groups[1].Value -notmatch '(?m)^name\s*=\s*"wardoff"\s*$') { + throw 'Cargo.toml must declare the wardoff package.' + } + $versions = [regex]::Matches($package[0].Groups[1].Value, '(?m)^version\s*=\s*"([^"]+)"\s*$') + if ($versions.Count -ne 1 -or $versions[0].Groups[1].Value -cne $version) { + throw 'Tag and Cargo.toml package version differ.' + } + $lock = Get-Content -LiteralPath (Join-Path $RepoRoot 'Cargo.lock') -Raw + $packages = @([regex]::Matches($lock, '(?ms)^\[\[package\]\]\s*\r?\n(.*?)(?=^\[\[package\]\]|\z)') | + Where-Object { $_.Groups[1].Value -match '(?m)^name\s*=\s*"wardoff"\s*$' }) + if ($packages.Count -ne 1) { throw 'Cargo.lock must contain exactly one wardoff package.' } + $locked = [regex]::Matches($packages[0].Groups[1].Value, '(?m)^version\s*=\s*"([^"]+)"\s*$') + if ($locked.Count -ne 1 -or $locked[0].Groups[1].Value -cne $version) { + throw 'Tag and Cargo.lock package version differ.' + } + [xml]$manifest = Get-Content -LiteralPath (Join-Path $RepoRoot 'wardoff.manifest') -Raw + $identity = $manifest.SelectNodes("/*[local-name()='assembly']/*[local-name()='assemblyIdentity']") + $level = $manifest.SelectNodes("//*[local-name()='requestedExecutionLevel']") + if ($identity.Count -ne 1 -or $identity[0].GetAttribute('name') -cne 'wardoff' -or + $identity[0].GetAttribute('version') -cne ($version + '.0') -or $level.Count -ne 1 -or + $level[0].GetAttribute('level') -cne 'asInvoker' -or $level[0].GetAttribute('uiAccess') -cne 'false') { + throw 'Manifest must match the package version and retain asInvoker/uiAccess=false.' + } + return $version +} + +function Resolve-ReleaseTagCommit { + param([string] $TagName, [AllowEmptyCollection()][string[]] $RemoteRefs) + if ($TagName -cnotmatch '^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$') { throw 'Invalid release tag.' } + $refName = 'refs/tags/' + $TagName + $base = @(); $peeled = @() + foreach ($line in $RemoteRefs) { + if ($line -notmatch '^([0-9a-fA-F]{40})\s+(\S+)$') { throw 'Malformed remote tag response.' } + $sha = $Matches[1]; $ref = $Matches[2] + if ($ref -ceq $refName) { $base += $sha } + elseif ($ref -ceq ($refName + '^{}')) { $peeled += $sha } + else { throw 'Unexpected remote tag reference.' } + } + if ($base.Count -ne 1 -or $peeled.Count -gt 1) { throw 'Remote tag is absent or ambiguous.' } + if ($peeled.Count -eq 1) { return $peeled[0].ToLowerInvariant() } + return $base[0].ToLowerInvariant() +} + +function Select-ReleaseCiRun { + param([AllowEmptyCollection()][object[]] $CiRuns, [string] $Repository, [string] $CandidateCommit) + $matching = @($CiRuns | Where-Object { + $_.head_sha -eq $CandidateCommit -and $_.head_branch -ceq 'main' -and $_.event -ceq 'push' -and + $_.head_repository.full_name -ceq $Repository -and + $_.path -cin @('.github/workflows/ci.yml', '.github/workflows/ci.yml@refs/heads/main') + }) + if ($matching.Count -eq 0) { throw 'No CI push/main run exists for the exact candidate commit.' } + foreach ($run in $matching) { + if (-not (Test-ReleasePositiveInteger $run.id) -or -not (Test-ReleasePositiveInteger $run.run_attempt)) { + throw 'CI run identity or attempt is invalid.' + } + $updated = [datetimeoffset]::MinValue + if ($run.updated_at -isnot [string] -or $run.updated_at -notmatch '(Z|\+00:00)$' -or + -not [datetimeoffset]::TryParse($run.updated_at, [ref]$updated)) { throw 'CI update timestamp is invalid or not UTC.' } + } + $selected = $matching | Sort-Object @{Expression = { [datetimeoffset]$_.updated_at }; Descending = $true}, @{Expression = { $_.id }; Descending = $true}, @{Expression = { $_.run_attempt }; Descending = $true} | Select-Object -First 1 + if ($selected.status -cne 'completed' -or $selected.conclusion -cne 'success') { + throw 'The latest candidate CI run has not completed successfully.' + } + return $selected +} + +function Assert-ReleaseDraftAvailable { + param([string] $TagName, [AllowEmptyCollection()][object[]] $Releases) + if (@($Releases | Where-Object { $_.tag_name -ceq $TagName }).Count -gt 0) { + throw 'A release or draft already exists for this tag; never overwrite it.' + } +} + +function Assert-ReleaseCandidate { + param([string] $RepoRoot, [string] $Repository, [string] $TagName, + [string] $CandidateCommit, [string] $RemoteTagCommit, [bool] $MainContainsCandidate, + [AllowEmptyCollection()][object[]] $CiRuns, [AllowEmptyCollection()][object[]] $CiJobs, + [AllowEmptyCollection()][object[]] $ExistingReleases) + if ($Repository -cne 'RomainROCH/Wardoff') { throw 'Release repository is not authorized.' } + if ($CandidateCommit -notmatch '^[0-9a-fA-F]{40}$' -or $RemoteTagCommit -notmatch '^[0-9a-fA-F]{40}$' -or + $CandidateCommit -ne $RemoteTagCommit) { throw 'Remote tag does not identify the tested commit.' } + if (-not $MainContainsCandidate) { throw 'Candidate commit is not reachable from main.' } + $version = Get-ReleaseVersion -RepoRoot $RepoRoot -TagName $TagName + $run = Select-ReleaseCiRun -CiRuns $CiRuns -Repository $Repository -CandidateCommit $CandidateCommit + if ($CiJobs.Count -eq 0) { throw 'CI jobs are missing.' } + foreach ($job in $CiJobs) { + if ($job.run_id -ne $run.id -or $job.head_sha -ne $CandidateCommit -or -not (Test-ReleasePositiveInteger $job.run_id) -or + $job.status -cne 'completed' -or $job.conclusion -cne 'success') { throw 'A candidate CI job is not successful.' } + } + $windows = @($CiJobs | Where-Object { $_.name -ceq 'ci (windows-latest, stable)' }) + if ($windows.Count -ne 1) { throw 'Required Windows CI job is missing or ambiguous.' } + foreach ($name in @('Full local checks', 'Developer tooling tests', 'Smoke safety tests', 'Release guard tests', 'Smoke tests')) { + $step = @($windows[0].steps | Where-Object { $_.name -ceq $name }) + if ($step.Count -ne 1 -or $step[0].status -cne 'completed' -or $step[0].conclusion -cne 'success') { + throw "Required CI step is absent, skipped or failed: $name." + } + } + Assert-ReleaseDraftAvailable -TagName $TagName -Releases $ExistingReleases + return [pscustomobject]@{ Tag = $TagName; Version = $version; Commit = $CandidateCommit.ToLowerInvariant(); CiRunId = $run.id; CiAttempt = $run.run_attempt } +} + +function Assert-ReleaseArtifact { + param([string] $PackageDirectory, [string] $CandidateCommit, [string] $TagName, + [string] $ExpectedSha256, $ExpectedBuildRunId) + if ($CandidateCommit -notmatch '^[0-9a-fA-F]{40}$' -or $ExpectedSha256 -notmatch '^[0-9a-fA-F]{64}$' -or + $TagName -cnotmatch '^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$' -or + -not (Test-ReleasePositiveInteger $ExpectedBuildRunId)) { throw 'Artifact expectations are invalid.' } + $binary = Join-Path $PackageDirectory 'wardoff.exe' + $info = Get-Content -LiteralPath (Join-Path $PackageDirectory 'build-info.json') -Raw | ConvertFrom-Json + $hash = (Get-FileHash -LiteralPath $binary -Algorithm SHA256).Hash + $checksum = @(Get-Content -LiteralPath (Join-Path $PackageDirectory 'SHA256SUMS.txt')) + if ($checksum.Count -ne 1 -or $checksum[0] -cnotmatch '^([0-9a-fA-F]{64}) wardoff\.exe$' -or + $Matches[1] -ne $ExpectedSha256) { throw 'Package checksum file differs from the checked binary.' } + if ($hash -ne $ExpectedSha256 -or $info.sha256 -ne $ExpectedSha256 -or + $info.source_commit -ne $CandidateCommit -or $info.tag -cne $TagName -or + $info.version -cne $TagName.Substring(1) -or $info.target -cne 'x86_64-pc-windows-msvc' -or + $info.unsigned -isnot [bool] -or -not $info.unsigned -or + -not (Test-ReleasePositiveInteger $info.build_run_id) -or $info.build_run_id -ne $ExpectedBuildRunId -or + -not (Test-ReleasePositiveInteger $info.ci_run_id)) { throw 'Artifact hash or build metadata differs from the validated candidate.' } + return $info +} + +function Assert-ReleaseTransferInputs { + param([string] $ArtifactId, [string] $Sha256) + if ($ArtifactId -cnotmatch '^[1-9][0-9]*$' -or $Sha256 -cnotmatch '^[0-9a-fA-F]{64}$') { + throw 'Exact artifact ID and binary SHA256 are required before downloading.' + } +} diff --git a/tests/release_guards.ps1 b/tests/release_guards.ps1 new file mode 100644 index 0000000..59ec3f1 --- /dev/null +++ b/tests/release_guards.ps1 @@ -0,0 +1,510 @@ +[CmdletBinding()] +param() + +$ErrorActionPreference = 'Stop' +$guardPath = Join-Path $PSScriptRoot '..\scripts\release-guard.ps1' +$fixtureRoot = Join-Path ([IO.Path]::GetTempPath()) ('wardoff-release-guard-' + [Guid]::NewGuid().ToString('N')) +$repoFixture = Join-Path $fixtureRoot 'repo' +$packageFixture = Join-Path $fixtureRoot 'package' +$repository = 'RomainROCH/Wardoff' +$candidateCommit = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +$otherCommit = 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' +$tagName = 'v0.2.1' +$script:caseCount = 0 + +function Assert-True { + param([bool]$Condition, [string]$Message) + if (-not $Condition) { throw $Message } +} + +function Assert-Equal { + param($Actual, $Expected, [string]$Message) + if ($Actual -cne $Expected) { throw "$Message Expected '$Expected', received '$Actual'." } +} + +function Assert-Throws { + param([scriptblock]$Action) + $caught = $false + try { & $Action | Out-Null } catch { $caught = $true } + if (-not $caught) { throw 'Expected the release guard to reject the fixture.' } +} + +function Invoke-TestCase { + param([string]$Name, [scriptblock]$Action) + try { & $Action } catch { throw "Release guard test failed [$Name]: $($_.Exception.Message)" } + $script:caseCount++ +} + +function Write-VersionFixture { + param( + [string]$TomlVersion = '0.2.1', + [string]$LockVersion = '0.2.1', + [string]$ManifestVersion = '0.2.1.0', + [string]$Level = 'asInvoker', + [string]$UiAccess = 'false', + [string]$PackageName = 'wardoff', + [string]$LockPackageName = 'wardoff' + ) + New-Item -ItemType Directory -Path $repoFixture -Force | Out-Null + Set-Content -LiteralPath (Join-Path $repoFixture 'Cargo.toml') -Encoding utf8 -Value @" +[package] +name = "$PackageName" +version = "$TomlVersion" +edition = "2021" + +[dependencies] +some_dependency = "9.8.7" +"@ + Set-Content -LiteralPath (Join-Path $repoFixture 'Cargo.lock') -Encoding utf8 -Value @" +version = 4 + +[[package]] +name = "some_dependency" +version = "9.8.7" + +[[package]] +name = "$LockPackageName" +version = "$LockVersion" +dependencies = ["some_dependency"] +"@ + Set-Content -LiteralPath (Join-Path $repoFixture 'wardoff.manifest') -Encoding utf8 -Value @" + + + + + + + +"@ +} + +function New-CiRun { + param( + [long]$Id = 73, + [string]$Commit = $candidateCommit, + [string]$Branch = 'main', + [string]$Event = 'push', + [string]$Path = '.github/workflows/ci.yml', + [string]$RepositoryName = $repository, + [string]$Status = 'completed', + [string]$Conclusion = 'success', + [int]$Attempt = 1, + [string]$UpdatedAt + ) + if (-not $PSBoundParameters.ContainsKey('UpdatedAt')) { + $UpdatedAt = [datetime]::new(2026, 1, 1, 0, 0, 0, [DateTimeKind]::Utc).AddSeconds($Id).ToString('yyyy-MM-ddTHH:mm:ssZ', [Globalization.CultureInfo]::InvariantCulture) + } + return [pscustomobject]@{ + id = $Id; head_sha = $Commit; head_branch = $Branch; event = $Event + path = $Path; head_repository = [pscustomobject]@{ full_name = $RepositoryName } + status = $Status; conclusion = $Conclusion; run_attempt = $Attempt; updated_at = $UpdatedAt + } +} + +function New-CiJob { + param([long]$RunId = 73) + return [pscustomobject]@{ + name = 'ci (windows-latest, stable)'; run_id = $RunId; head_sha = $candidateCommit; status = 'completed'; conclusion = 'success' + steps = @('Full local checks', 'Developer tooling tests', 'Smoke safety tests', 'Release guard tests', 'Smoke tests') | ForEach-Object { + [pscustomobject]@{ name = $_; status = 'completed'; conclusion = 'success' } + } + } +} + +function Invoke-CandidateFixture { + param([hashtable]$Overrides = @{}) + $arguments = @{ + RepoRoot = $repoFixture; Repository = $repository; TagName = $tagName + CandidateCommit = $candidateCommit; RemoteTagCommit = $candidateCommit + MainContainsCandidate = $true; CiRuns = @((New-CiRun)); CiJobs = @((New-CiJob)) + ExistingReleases = @() + } + foreach ($key in $Overrides.Keys) { $arguments[$key] = $Overrides[$key] } + return Assert-ReleaseCandidate @arguments +} + +function Write-ArtifactFixture { + New-Item -ItemType Directory -Path $packageFixture -Force | Out-Null + # Deliberately not an executable. These tests only validate bytes and metadata. + [IO.File]::WriteAllBytes((Join-Path $packageFixture 'wardoff.exe'), [Text.Encoding]::ASCII.GetBytes('release guard fixture - never execute')) + $hash = (Get-FileHash -LiteralPath (Join-Path $packageFixture 'wardoff.exe') -Algorithm SHA256).Hash + $info = [pscustomobject]@{ + source_commit = $candidateCommit; tag = $tagName; version = '0.2.1' + target = 'x86_64-pc-windows-msvc'; unsigned = $true + build_run_id = 91; ci_run_id = 73; sha256 = $hash + } + Write-ArtifactInfo -Info $info + Set-Content -LiteralPath (Join-Path $packageFixture 'SHA256SUMS.txt') -Encoding ascii -Value "$hash wardoff.exe" + return $info +} + +function Write-ArtifactInfo { + param($Info) + $Info | ConvertTo-Json -Depth 5 | Set-Content -LiteralPath (Join-Path $packageFixture 'build-info.json') -Encoding utf8 +} + +function Invoke-ArtifactFixture { + param([string]$ExpectedSha256, [hashtable]$Overrides = @{}) + $arguments = @{ + PackageDirectory = $packageFixture; CandidateCommit = $candidateCommit; TagName = $tagName + ExpectedSha256 = $ExpectedSha256; ExpectedBuildRunId = 91 + } + foreach ($key in $Overrides.Keys) { $arguments[$key] = $Overrides[$key] } + Assert-ReleaseArtifact @arguments | Out-Null +} + +try { + Invoke-TestCase 'exception assertion rejects a non-throwing action' { + $canaryCaught = $false + try { Assert-Throws {} } catch { $canaryCaught = $true } + Assert-True $canaryCaught 'Assert-Throws accepted an action that did not throw.' + } + if (-not (Test-Path -LiteralPath $guardPath -PathType Leaf)) { + throw 'Release guard implementation is missing: scripts/release-guard.ps1.' + } + . $guardPath + foreach ($functionName in @('Get-ReleaseVersion', 'Resolve-ReleaseTagCommit', 'Select-ReleaseCiRun', 'Assert-ReleaseDraftAvailable', 'Assert-ReleaseCandidate', 'Assert-ReleaseArtifact')) { + if (-not (Get-Command -Name $functionName -CommandType Function -ErrorAction SilentlyContinue)) { + throw "Release guard function is missing: $functionName." + } + } + New-Item -ItemType Directory -Path $fixtureRoot -Force | Out-Null + Write-VersionFixture + + Invoke-TestCase 'matching package lock and namespaced manifest' { + Assert-Equal (Get-ReleaseVersion -RepoRoot $repoFixture -TagName $tagName) '0.2.1' 'Matching versions were rejected.' + } + foreach ($invalidTag in @('', '0.2.1', 'V0.2.1', 'v00.2.1', 'v0.02.1', 'v0.2.01', 'v0.2', 'v0.2.1.0', 'v0.2.1-beta.1', 'v0.2.1+build', ' v0.2.1', 'v0.2.1 ', "v0.2.1`n")) { + Invoke-TestCase "strict semantic tag rejects [$invalidTag]" { + Assert-Throws { Get-ReleaseVersion -RepoRoot $repoFixture -TagName $invalidTag } + } + } + Invoke-TestCase 'zero components remain valid' { + Write-VersionFixture -TomlVersion '0.0.0' -LockVersion '0.0.0' -ManifestVersion '0.0.0.0' + Assert-Equal (Get-ReleaseVersion -RepoRoot $repoFixture -TagName 'v0.0.0') '0.0.0' 'Zero components were rejected.' + } + foreach ($versions in @( + @{ TomlVersion = '0.2.0' }, @{ LockVersion = '0.2.0' }, @{ ManifestVersion = '0.2.0.0' }, + @{ ManifestVersion = '0.2.1.1' }, @{ ManifestVersion = '0.2.1' }, @{ Level = 'requireAdministrator' }, @{ UiAccess = 'true' }, + @{ PackageName = 'other' }, @{ LockPackageName = 'other' } + )) { + Invoke-TestCase ('version fixture rejects ' + ($versions.Keys -join ',')) { + Write-VersionFixture @versions + Assert-Throws { Get-ReleaseVersion -RepoRoot $repoFixture -TagName $tagName } + } + } + foreach ($missingFile in @('Cargo.toml', 'Cargo.lock', 'wardoff.manifest')) { + Invoke-TestCase "missing version file $missingFile" { + Write-VersionFixture + Remove-Item -LiteralPath (Join-Path $repoFixture $missingFile) + Assert-Throws { Get-ReleaseVersion -RepoRoot $repoFixture -TagName $tagName } + } + } + Invoke-TestCase 'malformed manifest' { + Write-VersionFixture + Set-Content -LiteralPath (Join-Path $repoFixture 'wardoff.manifest') -Encoding utf8 -Value '' + Assert-Throws { Get-ReleaseVersion -RepoRoot $repoFixture -TagName $tagName } + } + Write-VersionFixture + + Invoke-TestCase 'lightweight remote tag' { + Assert-Equal (Resolve-ReleaseTagCommit -TagName $tagName -RemoteRefs @("$candidateCommit`trefs/tags/$tagName")) $candidateCommit 'Lightweight tag SHA differs.' + } + Invoke-TestCase 'annotated remote tag uses peeled commit' { + $refs = @("$otherCommit`trefs/tags/$tagName", "$candidateCommit`trefs/tags/$tagName^{}") + Assert-Equal (Resolve-ReleaseTagCommit -TagName $tagName -RemoteRefs $refs) $candidateCommit 'Annotated tag was not peeled.' + } + foreach ($remoteRefs in @( + @{ Name = 'missing'; Refs = @() }, + @{ Name = 'different tag'; Refs = @("$candidateCommit`trefs/tags/v0.2.10") }, + @{ Name = 'short hash'; Refs = @("aaaa`trefs/tags/$tagName") }, + @{ Name = 'non hexadecimal hash'; Refs = @('gggggggggggggggggggggggggggggggggggggggg refs/tags/v0.2.1') }, + @{ Name = 'malformed line'; Refs = @("$candidateCommit refs/tags/$tagName extra") }, + @{ Name = 'conflicting duplicate'; Refs = @("$candidateCommit`trefs/tags/$tagName", "$otherCommit`trefs/tags/$tagName") } + )) { + Invoke-TestCase ('remote tag rejects ' + $remoteRefs.Name) { + Assert-Throws { Resolve-ReleaseTagCommit -TagName $tagName -RemoteRefs $remoteRefs.Refs } + } + } + + Invoke-TestCase 'exact successful main push CI' { + $run = Select-ReleaseCiRun -CiRuns @((New-CiRun)) -Repository $repository -CandidateCommit $candidateCommit + Assert-Equal $run.id 73 'The expected CI run was not selected.' + } + Invoke-TestCase 'main workflow path suffix' { + $run = Select-ReleaseCiRun -CiRuns @((New-CiRun -Path '.github/workflows/ci.yml@refs/heads/main')) -Repository $repository -CandidateCommit $candidateCommit + Assert-Equal $run.id 73 'The authorized workflow suffix was rejected.' + } + foreach ($runArguments in @( + @{ Commit = $otherCommit }, @{ Branch = 'dev' }, @{ Event = 'pull_request' }, + @{ Path = '.github/workflows/release.yml' }, @{ Path = '.github/workflows/ci.yml@refs/heads/dev' }, + @{ Path = '.github/workflows/ci.yml@refs/heads/main/extra' }, @{ RepositoryName = 'other/Wardoff' }, + @{ Attempt = 0 }, @{ Status = 'in_progress'; Conclusion = '' }, @{ Conclusion = 'failure' }, + @{ Conclusion = 'cancelled' }, @{ Conclusion = 'skipped' } + )) { + Invoke-TestCase ('CI rejects ' + ($runArguments.Keys -join ',')) { + Assert-Throws { Select-ReleaseCiRun -CiRuns @((New-CiRun @runArguments)) -Repository $repository -CandidateCommit $candidateCommit } + } + } + Invoke-TestCase 'CI evidence absent' { + Assert-Throws { Select-ReleaseCiRun -CiRuns @() -Repository $repository -CandidateCommit $candidateCommit } + } + foreach ($invalidRunId in @(0, -1, $true, '73')) { + Invoke-TestCase "CI run ID rejects invalid value [$invalidRunId]" { + $run = New-CiRun + $run.id = $invalidRunId + Assert-Throws { Select-ReleaseCiRun -CiRuns @($run) -Repository $repository -CandidateCommit $candidateCommit } + } + } + Invoke-TestCase 'newest eligible run is selected by update timestamp' { + $run = Select-ReleaseCiRun -CiRuns @((New-CiRun -Id 9), (New-CiRun -Id 101), (New-CiRun -Id 88)) -Repository $repository -CandidateCommit $candidateCommit + Assert-Equal $run.id 101 'The newest CI run was not selected.' + } + Invoke-TestCase 'recent failed rerun of lower ID invalidates higher ID green' { + $runs = @( + (New-CiRun -Id 74 -UpdatedAt '2026-10-05T01:00:00Z'), + (New-CiRun -Id 70 -Attempt 2 -Conclusion 'failure' -UpdatedAt '2026-10-05T02:00:00Z') + ) + Assert-Throws { Select-ReleaseCiRun -CiRuns $runs -Repository $repository -CandidateCommit $candidateCommit } + } + Invoke-TestCase 'recent running rerun of lower ID invalidates higher ID green' { + $runs = @( + (New-CiRun -Id 74 -UpdatedAt '2026-10-05T01:00:00Z'), + (New-CiRun -Id 70 -Attempt 2 -Status 'in_progress' -Conclusion '' -UpdatedAt '2026-10-05T02:00:00Z') + ) + Assert-Throws { Select-ReleaseCiRun -CiRuns $runs -Repository $repository -CandidateCommit $candidateCommit } + } + Invoke-TestCase 'recent successful rerun of lower ID is selected' { + $runs = @( + (New-CiRun -Id 74 -UpdatedAt '2026-10-05T01:00:00Z'), + (New-CiRun -Id 70 -Attempt 2 -UpdatedAt '2026-10-05T02:00:00Z') + ) + $run = Select-ReleaseCiRun -CiRuns $runs -Repository $repository -CandidateCommit $candidateCommit + Assert-Equal $run.id 70 'The most recently updated rerun was not selected.' + Assert-Equal $run.run_attempt 2 'The expected rerun attempt was not selected.' + } + Invoke-TestCase 'equal update timestamps use run ID as tie breaker' { + $runs = @((New-CiRun -Id 70 -UpdatedAt '2026-10-05T01:00:00Z'), (New-CiRun -Id 74 -UpdatedAt '2026-10-05T01:00:00Z')) + $run = Select-ReleaseCiRun -CiRuns $runs -Repository $repository -CandidateCommit $candidateCommit + Assert-Equal $run.id 74 'Equal timestamps were not ordered by run ID.' + } + Invoke-TestCase 'equal update timestamp and ID use attempt as tie breaker' { + $runs = @((New-CiRun -Id 74 -Attempt 1 -UpdatedAt '2026-10-05T01:00:00Z'), (New-CiRun -Id 74 -Attempt 2 -UpdatedAt '2026-10-05T01:00:00Z')) + $run = Select-ReleaseCiRun -CiRuns $runs -Repository $repository -CandidateCommit $candidateCommit + Assert-Equal $run.run_attempt 2 'Equal timestamps and IDs were not ordered by attempt.' + } + Invoke-TestCase 'CI run without update timestamp is rejected' { + $run = New-CiRun + $run.PSObject.Properties.Remove('updated_at') + Assert-Throws { Select-ReleaseCiRun -CiRuns @($run) -Repository $repository -CandidateCommit $candidateCommit } + } + foreach ($invalidUpdatedAt in @('', 'not-a-timestamp', '2026-13-05T01:00:00Z', '2026-10-05T01:00:00')) { + Invoke-TestCase "CI run rejects malformed update timestamp [$invalidUpdatedAt]" { + Assert-Throws { Select-ReleaseCiRun -CiRuns @((New-CiRun -UpdatedAt $invalidUpdatedAt)) -Repository $repository -CandidateCommit $candidateCommit } + } + } + foreach ($latestConclusion in @('failure', 'cancelled', 'skipped')) { + Invoke-TestCase "newer $latestConclusion run invalidates old green" { + Assert-Throws { Select-ReleaseCiRun -CiRuns @((New-CiRun -Id 70), (New-CiRun -Id 74 -Conclusion $latestConclusion)) -Repository $repository -CandidateCommit $candidateCommit } + } + } + Invoke-TestCase 'newer running CI invalidates old green' { + Assert-Throws { Select-ReleaseCiRun -CiRuns @((New-CiRun -Id 70), (New-CiRun -Id 74 -Status 'in_progress' -Conclusion '')) -Repository $repository -CandidateCommit $candidateCommit } + } + Invoke-TestCase 'unrelated newer commit does not replace exact SHA evidence' { + $run = Select-ReleaseCiRun -CiRuns @((New-CiRun -Id 70), (New-CiRun -Id 74 -Commit $otherCommit -Conclusion 'failure')) -Repository $repository -CandidateCommit $candidateCommit + Assert-Equal $run.id 70 'Unrelated SHA replaced exact candidate evidence.' + } + + Invoke-TestCase 'no existing release' { Assert-ReleaseDraftAvailable -TagName $tagName -Releases @() } + foreach ($draftState in @($true, $false)) { + Invoke-TestCase "existing release rejected draft=$draftState" { + Assert-Throws { Assert-ReleaseDraftAvailable -TagName $tagName -Releases @([pscustomobject]@{ tag_name = $tagName; draft = $draftState; assets = @() }) } + } + } + Invoke-TestCase 'release for other tag allowed' { + Assert-ReleaseDraftAvailable -TagName $tagName -Releases @([pscustomobject]@{ tag_name = 'v0.2.0'; draft = $false; assets = @([pscustomobject]@{ name = 'wardoff.exe' }) }) + } + + Invoke-TestCase 'complete candidate returns bound metadata' { + $candidate = Invoke-CandidateFixture + Assert-Equal $candidate.Tag $tagName 'Candidate tag differs.' + Assert-Equal $candidate.Version '0.2.1' 'Candidate version differs.' + Assert-Equal $candidate.Commit $candidateCommit 'Candidate commit differs.' + Assert-Equal $candidate.CiRunId 73 'Candidate CI run differs.' + } + foreach ($overrides in @( + @{ Repository = 'other/Wardoff' }, @{ CandidateCommit = 'aaaa' }, + @{ CandidateCommit = 'gggggggggggggggggggggggggggggggggggggggg' }, + @{ RemoteTagCommit = 'aaaa' }, @{ RemoteTagCommit = $otherCommit }, + @{ MainContainsCandidate = $false }, @{ CiRuns = @() }, @{ CiJobs = @() } + )) { + Invoke-TestCase ('candidate rejects ' + ($overrides.Keys -join ',')) { + Assert-Throws { Invoke-CandidateFixture -Overrides $overrides } + } + } + Invoke-TestCase 'moved annotated tag invalidates tested commit' { + $movedTag = Resolve-ReleaseTagCommit -TagName $tagName -RemoteRefs @("$candidateCommit`trefs/tags/$tagName", "$otherCommit`trefs/tags/$tagName^{}") + Assert-Throws { Invoke-CandidateFixture -Overrides @{ RemoteTagCommit = $movedTag } } + } + Invoke-TestCase 'candidate rejects CI job for another commit' { + $job = New-CiJob + $job.head_sha = $otherCommit + Assert-Throws { Invoke-CandidateFixture -Overrides @{ CiJobs = @($job) } } + } + Invoke-TestCase 'candidate rejects CI job without commit SHA' { + $job = New-CiJob + $job.PSObject.Properties.Remove('head_sha') + Assert-Throws { Invoke-CandidateFixture -Overrides @{ CiJobs = @($job) } } + } + foreach ($jobProblem in @('wrong run', 'wrong job', 'failure', 'running')) { + Invoke-TestCase "candidate rejects $jobProblem" { + $job = New-CiJob + switch ($jobProblem) { + 'wrong run' { $job.run_id = 74 } + 'wrong job' { $job.name = 'ci (ubuntu-latest, stable)' } + 'failure' { $job.conclusion = 'failure' } + 'running' { $job.status = 'in_progress' } + } + Assert-Throws { Invoke-CandidateFixture -Overrides @{ CiJobs = @($job) } } + } + } + foreach ($stepName in @('Full local checks', 'Developer tooling tests', 'Smoke safety tests', 'Release guard tests', 'Smoke tests')) { + foreach ($stepProblem in @('missing', 'failure', 'skipped', 'running')) { + Invoke-TestCase "candidate rejects $stepName $stepProblem" { + $job = New-CiJob + if ($stepProblem -eq 'missing') { + $job.steps = @($job.steps | Where-Object { $_.name -cne $stepName }) + } else { + $step = $job.steps | Where-Object { $_.name -ceq $stepName } + if ($stepProblem -eq 'running') { $step.status = 'in_progress' } else { $step.conclusion = $stepProblem } + } + Assert-Throws { Invoke-CandidateFixture -Overrides @{ CiJobs = @($job) } } + } + } + } + Invoke-TestCase 'other failed CI job invalidates candidate' { + $failedJob = [pscustomobject]@{ name = 'additional checks'; run_id = 73; status = 'completed'; conclusion = 'failure'; steps = @() } + Assert-Throws { Invoke-CandidateFixture -Overrides @{ CiJobs = @((New-CiJob), $failedJob) } } + } + Invoke-TestCase 'existing public candidate release cannot be overwritten' { + Assert-Throws { Invoke-CandidateFixture -Overrides @{ ExistingReleases = @([pscustomobject]@{ tag_name = $tagName; draft = $false; assets = @([pscustomobject]@{ name = 'wardoff.exe' }) }) } } + } + Invoke-TestCase 'existing draft candidate release cannot be overwritten' { + Assert-Throws { Invoke-CandidateFixture -Overrides @{ ExistingReleases = @([pscustomobject]@{ tag_name = $tagName; draft = $true; assets = @() }) } } + } + Invoke-TestCase 'candidate enforces the version fixture guard' { + Write-VersionFixture -LockVersion '0.2.0' + Assert-Throws { Invoke-CandidateFixture } + Write-VersionFixture + } + + Invoke-TestCase 'exact artifact hash and metadata' { + $info = Write-ArtifactFixture + Invoke-ArtifactFixture -ExpectedSha256 $info.sha256 + } + foreach ($propertyCase in @( + @{ Field = 'source_commit'; Value = $otherCommit }, @{ Field = 'tag'; Value = 'v0.2.0' }, + @{ Field = 'version'; Value = '0.2.0' }, @{ Field = 'target'; Value = 'x86_64-pc-windows-gnu' }, + @{ Field = 'unsigned'; Value = $false }, @{ Field = 'unsigned'; Value = 'true' }, + @{ Field = 'build_run_id'; Value = 92 }, @{ Field = 'build_run_id'; Value = 0 }, + @{ Field = 'build_run_id'; Value = -1 }, @{ Field = 'build_run_id'; Value = $true }, + @{ Field = 'build_run_id'; Value = '91' }, @{ Field = 'ci_run_id'; Value = 0 }, + @{ Field = 'ci_run_id'; Value = -1 }, @{ Field = 'ci_run_id'; Value = $true }, + @{ Field = 'ci_run_id'; Value = '73' }, @{ Field = 'sha256'; Value = ('0' * 64) } + )) { + Invoke-TestCase ('artifact rejects ' + $propertyCase.Field + '=' + $propertyCase.Value) { + $info = Write-ArtifactFixture + $expectedHash = $info.sha256 + $info.($propertyCase.Field) = $propertyCase.Value + Write-ArtifactInfo -Info $info + Assert-Throws { Invoke-ArtifactFixture -ExpectedSha256 $expectedHash } + } + } + foreach ($fieldName in @('source_commit', 'tag', 'version', 'target', 'unsigned', 'build_run_id', 'ci_run_id', 'sha256')) { + Invoke-TestCase "artifact rejects missing metadata $fieldName" { + $info = Write-ArtifactFixture + $expectedHash = $info.sha256 + $info.PSObject.Properties.Remove($fieldName) + Write-ArtifactInfo -Info $info + Assert-Throws { Invoke-ArtifactFixture -ExpectedSha256 $expectedHash } + } + } + Invoke-TestCase 'artifact bytes changed after verification' { + $info = Write-ArtifactFixture + [IO.File]::WriteAllBytes((Join-Path $packageFixture 'wardoff.exe'), [byte[]](1, 2, 3)) + Assert-Throws { Invoke-ArtifactFixture -ExpectedSha256 $info.sha256 } + } + Invoke-TestCase 'trusted output hash differs from artifact and metadata' { + $info = Write-ArtifactFixture + Assert-Throws { Invoke-ArtifactFixture -ExpectedSha256 ('0' * 64) } + } + Invoke-TestCase 'expected build run differs' { + $info = Write-ArtifactFixture + Assert-Throws { Invoke-ArtifactFixture -ExpectedSha256 $info.sha256 -Overrides @{ ExpectedBuildRunId = 92 } } + } + Invoke-TestCase 'malformed artifact metadata' { + $info = Write-ArtifactFixture + Set-Content -LiteralPath (Join-Path $packageFixture 'build-info.json') -Encoding utf8 -Value '{broken' + Assert-Throws { Invoke-ArtifactFixture -ExpectedSha256 $info.sha256 } + } + foreach ($missingArtifact in @('wardoff.exe', 'build-info.json', 'SHA256SUMS.txt')) { + Invoke-TestCase "artifact rejects missing $missingArtifact" { + $info = Write-ArtifactFixture + Remove-Item -LiteralPath (Join-Path $packageFixture $missingArtifact) + Assert-Throws { Invoke-ArtifactFixture -ExpectedSha256 $info.sha256 } + } + } + Invoke-TestCase 'checksum file hash changed independently' { + $info = Write-ArtifactFixture + Set-Content -LiteralPath (Join-Path $packageFixture 'SHA256SUMS.txt') -Encoding ascii -Value ((('0' * 64) + ' wardoff.exe')) + Assert-Throws { Invoke-ArtifactFixture -ExpectedSha256 $info.sha256 } + } + Invoke-TestCase 'checksum file names another asset' { + $info = Write-ArtifactFixture + Set-Content -LiteralPath (Join-Path $packageFixture 'SHA256SUMS.txt') -Encoding ascii -Value "$($info.sha256) other.exe" + Assert-Throws { Invoke-ArtifactFixture -ExpectedSha256 $info.sha256 } + } + Invoke-TestCase 'checksum file contains multiple lines' { + $info = Write-ArtifactFixture + Set-Content -LiteralPath (Join-Path $packageFixture 'SHA256SUMS.txt') -Encoding ascii -Value @("$($info.sha256) wardoff.exe", "$($info.sha256) other.exe") + Assert-Throws { Invoke-ArtifactFixture -ExpectedSha256 $info.sha256 } + } + + Invoke-TestCase 'valid transfer inputs produce no output' { + $result = @(Assert-ReleaseTransferInputs -ArtifactId '123' -Sha256 ('a' * 64)) + Assert-Equal $result.Count 0 'The transfer input guard produced unexpected output.' + } + foreach ($invalidArtifactId in @('', '0', '-1', 'not-an-id', '1.5', '1e3', ' 73', '73 ')) { + Invoke-TestCase "transfer guard rejects artifact ID [$invalidArtifactId]" { + Assert-Throws { Assert-ReleaseTransferInputs -ArtifactId $invalidArtifactId -Sha256 ('a' * 64) } + } + } + foreach ($invalidTransferHash in @('', 'abc', ('0' * 63), ('0' * 65), ('g' * 64))) { + Invoke-TestCase "transfer guard rejects malformed hash [$invalidTransferHash]" { + Assert-Throws { Assert-ReleaseTransferInputs -ArtifactId '123' -Sha256 $invalidTransferHash } + } + } + + Invoke-TestCase 'PowerShell parser accepts guard and tests' { + foreach ($path in @($guardPath, $PSCommandPath)) { + $syntaxErrors = $null + [System.Management.Automation.Language.Parser]::ParseFile($path, [ref]$null, [ref]$syntaxErrors) | Out-Null + if ($syntaxErrors) { throw (($syntaxErrors | ForEach-Object { $_.Message }) -join [Environment]::NewLine) } + } + } + Write-Output "Release guard tests passed ($script:caseCount cases)." +} finally { + # Remove only this test's generated directory after checking its resolved boundary. + if (Test-Path -LiteralPath $fixtureRoot) { + $resolvedFixture = (Resolve-Path -LiteralPath $fixtureRoot).Path + $tempRoot = [IO.Path]::GetFullPath([IO.Path]::GetTempPath()).TrimEnd('\') + $fixtureName = Split-Path -Leaf $resolvedFixture + if (-not ($resolvedFixture.StartsWith($tempRoot + '\', [StringComparison]::OrdinalIgnoreCase) -and + $fixtureName.StartsWith('wardoff-release-guard-', [StringComparison]::OrdinalIgnoreCase))) { + throw 'Refusing to remove a release fixture outside the temporary test boundary.' + } + Remove-Item -LiteralPath $resolvedFixture -Recurse -Force + } +} +exit 0 diff --git a/tests/smoke_helpers.ps1 b/tests/smoke_helpers.ps1 index 7ee0c9d..02fccd9 100644 --- a/tests/smoke_helpers.ps1 +++ b/tests/smoke_helpers.ps1 @@ -1,5 +1,19 @@ Set-StrictMode -Version Latest +function Invoke-SmokeReleaseBuild { + param([bool] $UseExistingBinary, [string] $BinaryPath, [scriptblock] $BuildAction) + if ($UseExistingBinary) { + if (-not (Test-Path -LiteralPath $BinaryPath -PathType Leaf)) { + throw "Expected an existing release binary at $BinaryPath; compilation is disabled." + } + return + } + $result = & $BuildAction + if ($result.ExitCode -ne 0) { + throw "cargo build --release --locked failed with exit code $($result.ExitCode)." + } +} + function Get-SmokeCurrentSessionId { return [System.Diagnostics.Process]::GetCurrentProcess().SessionId } @@ -108,3 +122,83 @@ function Test-SmokeAutostartTaskIdentity { [string]::Equals($ExpectedIdentity.Path, $ActualIdentity.Path, [System.StringComparison]::OrdinalIgnoreCase) -and [string]::Equals($ExpectedIdentity.WorkingDirectory, $ActualIdentity.WorkingDirectory, [System.StringComparison]::OrdinalIgnoreCase) } + +function Get-EmbeddedManifestContent { + param( + [Parameter(Mandatory = $true)] + [string] $Path + ) + + if (-not ('Wardoff.ManifestReader' -as [type])) { + Add-Type @" +using System; +using System.ComponentModel; +using System.Runtime.InteropServices; +using System.Text; + +namespace Wardoff { + public static class ManifestReader { + private const uint LOAD_LIBRARY_AS_DATAFILE = 0x00000002; + private static readonly IntPtr ManifestResourceId = new IntPtr(1); + private static readonly IntPtr ManifestResourceType = new IntPtr(24); + + [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + private static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hFile, uint dwFlags); + + [DllImport("kernel32.dll", SetLastError = true)] + private static extern IntPtr FindResource(IntPtr hModule, IntPtr lpName, IntPtr lpType); + + [DllImport("kernel32.dll", SetLastError = true)] + private static extern IntPtr LoadResource(IntPtr hModule, IntPtr hResInfo); + + [DllImport("kernel32.dll", SetLastError = true)] + private static extern IntPtr LockResource(IntPtr hResData); + + [DllImport("kernel32.dll", SetLastError = true)] + private static extern uint SizeofResource(IntPtr hModule, IntPtr hResInfo); + + [DllImport("kernel32.dll", SetLastError = true)] + private static extern bool FreeLibrary(IntPtr hModule); + + public static string ReadManifest(string path) { + var module = LoadLibraryEx(path, IntPtr.Zero, LOAD_LIBRARY_AS_DATAFILE); + if (module == IntPtr.Zero) { + throw new Win32Exception(Marshal.GetLastWin32Error(), "LoadLibraryEx failed for " + path); + } + + try { + var manifestInfo = FindResource(module, ManifestResourceId, ManifestResourceType); + if (manifestInfo == IntPtr.Zero) { + throw new Win32Exception(Marshal.GetLastWin32Error(), "FindResource could not locate the embedded manifest."); + } + + var manifestBytesLength = SizeofResource(module, manifestInfo); + if (manifestBytesLength == 0) { + throw new InvalidOperationException("The embedded manifest resource was empty."); + } + + var manifestHandle = LoadResource(module, manifestInfo); + if (manifestHandle == IntPtr.Zero) { + throw new Win32Exception(Marshal.GetLastWin32Error(), "LoadResource failed for the embedded manifest."); + } + + var manifestPointer = LockResource(manifestHandle); + if (manifestPointer == IntPtr.Zero) { + throw new Win32Exception(Marshal.GetLastWin32Error(), "LockResource failed for the embedded manifest."); + } + + var manifestBytes = new byte[(int)manifestBytesLength]; + Marshal.Copy(manifestPointer, manifestBytes, 0, (int)manifestBytesLength); + return Encoding.UTF8.GetString(manifestBytes); + } + finally { + FreeLibrary(module); + } + } + } +} +"@ + } + + return [Wardoff.ManifestReader]::ReadManifest([System.IO.Path]::GetFullPath($Path)) +} diff --git a/tests/smoke_safety.ps1 b/tests/smoke_safety.ps1 index 9a48ff7..5a27bcd 100644 --- a/tests/smoke_safety.ps1 +++ b/tests/smoke_safety.ps1 @@ -33,6 +33,26 @@ try { try { Assert-Throws {} 'canary' } catch { $canaryCaught = $true } Assert-True $canaryCaught 'Assert-Throws accepted an action that did not throw.' + $buildFixture = Join-Path ([IO.Path]::GetTempPath()) ('wardoff-smoke-build-' + [Guid]::NewGuid().ToString('N') + '.exe') + try { + [IO.File]::WriteAllBytes($buildFixture, [byte[]](1, 2, 3)) + $buildCalls = @{ Count = 0 } + $buildAction = { $buildCalls.Count++; return [pscustomobject]@{ ExitCode = 0 } } + Invoke-SmokeReleaseBuild -UseExistingBinary $true -BinaryPath $buildFixture -BuildAction $buildAction + Assert-True ($buildCalls.Count -eq 0) 'Existing-binary smoke unexpectedly invoked Cargo.' + Invoke-SmokeReleaseBuild -UseExistingBinary $false -BinaryPath $buildFixture -BuildAction $buildAction + Assert-True ($buildCalls.Count -eq 1) 'Ordinary smoke did not invoke its build action.' + Assert-Throws { + Invoke-SmokeReleaseBuild -UseExistingBinary $true -BinaryPath ($buildFixture + '.missing') -BuildAction $buildAction + } 'existing release binary' + Assert-True ($buildCalls.Count -eq 1) 'Missing-binary smoke fell back to compilation.' + Assert-Throws { + Invoke-SmokeReleaseBuild -UseExistingBinary $false -BinaryPath $buildFixture -BuildAction { return [pscustomobject]@{ ExitCode = 7 } } + } 'exit code 7' + } finally { + if (Test-Path -LiteralPath $buildFixture) { Remove-Item -LiteralPath $buildFixture -Force } + } + $probeState = @{ Calls = 0 } $emptyProbe = { $probeState.Calls++; return $false } Assert-SmokePreconditions -WardoffProcesses @() -CurrentSessionId 7 -BinaryPath $binaryPath -AutostartTaskProbe $emptyProbe diff --git a/tests/smoke_test.ps1 b/tests/smoke_test.ps1 index bd4be44..0ca777a 100644 --- a/tests/smoke_test.ps1 +++ b/tests/smoke_test.ps1 @@ -9,7 +9,7 @@ development use scripts/check.ps1 instead. See docs/MVP_VALIDATION_MATRIX.md for coverage and manual acceptance. #> [CmdletBinding()] -param() +param([switch] $UseExistingBinary) Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' @@ -561,85 +561,7 @@ function Get-RepoWardoffProcesses { ) } -function Get-EmbeddedManifestContent { - param( - [Parameter(Mandatory = $true)] - [string] $Path - ) - - if (-not ('Wardoff.ManifestReader' -as [type])) { - Add-Type @" -using System; -using System.ComponentModel; -using System.Runtime.InteropServices; -using System.Text; - -namespace Wardoff { - public static class ManifestReader { - private const uint LOAD_LIBRARY_AS_DATAFILE = 0x00000002; - private static readonly IntPtr ManifestResourceId = new IntPtr(1); - private static readonly IntPtr ManifestResourceType = new IntPtr(24); - - [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] - private static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hFile, uint dwFlags); - - [DllImport("kernel32.dll", SetLastError = true)] - private static extern IntPtr FindResource(IntPtr hModule, IntPtr lpName, IntPtr lpType); - - [DllImport("kernel32.dll", SetLastError = true)] - private static extern IntPtr LoadResource(IntPtr hModule, IntPtr hResInfo); - - [DllImport("kernel32.dll", SetLastError = true)] - private static extern IntPtr LockResource(IntPtr hResData); - - [DllImport("kernel32.dll", SetLastError = true)] - private static extern uint SizeofResource(IntPtr hModule, IntPtr hResInfo); - - [DllImport("kernel32.dll", SetLastError = true)] - private static extern bool FreeLibrary(IntPtr hModule); - - public static string ReadManifest(string path) { - var module = LoadLibraryEx(path, IntPtr.Zero, LOAD_LIBRARY_AS_DATAFILE); - if (module == IntPtr.Zero) { - throw new Win32Exception(Marshal.GetLastWin32Error(), "LoadLibraryEx failed for " + path); - } - - try { - var manifestInfo = FindResource(module, ManifestResourceId, ManifestResourceType); - if (manifestInfo == IntPtr.Zero) { - throw new Win32Exception(Marshal.GetLastWin32Error(), "FindResource could not locate the embedded manifest."); - } - - var manifestBytesLength = SizeofResource(module, manifestInfo); - if (manifestBytesLength == 0) { - throw new InvalidOperationException("The embedded manifest resource was empty."); - } - - var manifestHandle = LoadResource(module, manifestInfo); - if (manifestHandle == IntPtr.Zero) { - throw new Win32Exception(Marshal.GetLastWin32Error(), "LoadResource failed for the embedded manifest."); - } - var manifestPointer = LockResource(manifestHandle); - if (manifestPointer == IntPtr.Zero) { - throw new Win32Exception(Marshal.GetLastWin32Error(), "LockResource failed for the embedded manifest."); - } - - var manifestBytes = new byte[(int)manifestBytesLength]; - Marshal.Copy(manifestPointer, manifestBytes, 0, (int)manifestBytesLength); - return Encoding.UTF8.GetString(manifestBytes); - } - finally { - FreeLibrary(module); - } - } - } -} -"@ - } - - return [Wardoff.ManifestReader]::ReadManifest([System.IO.Path]::GetFullPath($Path)) -} function Wait-ForBinaryUnlock { param( @@ -832,9 +754,10 @@ Assert-SmokePreflight try { Stop-RepoWardoffProcesses - Invoke-TestCase 'cargo build --release succeeds' { - $result = Invoke-ExternalCommand -FilePath 'cargo' -Arguments @('build', '--release') - Assert-Condition ($result.ExitCode -eq 0) "cargo build --release failed with exit code $($result.ExitCode)." + Invoke-TestCase 'release binary preparation succeeds' { + Invoke-SmokeReleaseBuild -UseExistingBinary $UseExistingBinary.IsPresent -BinaryPath $binaryPath -BuildAction { + Invoke-ExternalCommand -FilePath 'cargo' -Arguments @('build', '--release', '--locked') + } } Invoke-TestCase 'target\release\wardoff.exe exists after the release build' { diff --git a/wardoff.manifest b/wardoff.manifest index be6fa88..4a8cab7 100644 --- a/wardoff.manifest +++ b/wardoff.manifest @@ -1,7 +1,7 @@ Date: Mon, 5 Oct 2026 08:30:58 +0200 Subject: [PATCH 11/15] fix: preserve GitHub timestamp strings across PowerShell versions --- scripts/release-candidate.ps1 | 2 +- scripts/release-guard.ps1 | 11 +++++++++++ tests/release_guards.ps1 | 13 +++++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/scripts/release-candidate.ps1 b/scripts/release-candidate.ps1 index 5476e06..946174f 100644 --- a/scripts/release-candidate.ps1 +++ b/scripts/release-candidate.ps1 @@ -31,7 +31,7 @@ function Read-ReleaseApiPages { param([string] $Endpoint) $json = & gh api --method GET --paginate --slurp $Endpoint if ($LASTEXITCODE -ne 0) { throw 'Release GitHub read failed; no fallback is allowed.' } - return ($json | ConvertFrom-Json) + return (ConvertFrom-ReleaseApiJson -Json ($json -join "`n")) } $head = @(Invoke-ReleaseGit @('rev-parse', 'HEAD'))[0] diff --git a/scripts/release-guard.ps1 b/scripts/release-guard.ps1 index a2382f7..95e2054 100644 --- a/scripts/release-guard.ps1 +++ b/scripts/release-guard.ps1 @@ -1,6 +1,17 @@ # Pure release policy. Network reads and binary execution belong to the driver. Set-StrictMode -Version Latest +function ConvertFrom-ReleaseApiJson { + param([Parameter(Mandatory = $true)][string] $Json) + # PowerShell 7.5+ otherwise converts ISO dates to DateTime. Keep the GitHub + # timestamp text intact for the strict UTC check and rerun comparison. + $jsonOptions = @{} + if ((Get-Command ConvertFrom-Json).Parameters.ContainsKey('DateKind')) { + $jsonOptions.DateKind = 'String' + } + return ($Json | ConvertFrom-Json @jsonOptions) +} + function Test-ReleasePositiveInteger { param($Value) return (($Value -is [int] -or $Value -is [long] -or $Value -is [uint32] -or $Value -is [uint64]) -and $Value -gt 0) diff --git a/tests/release_guards.ps1 b/tests/release_guards.ps1 index 59ec3f1..569060e 100644 --- a/tests/release_guards.ps1 +++ b/tests/release_guards.ps1 @@ -231,6 +231,19 @@ try { $run = Select-ReleaseCiRun -CiRuns @((New-CiRun)) -Repository $repository -CandidateCommit $candidateCommit Assert-Equal $run.id 73 'The expected CI run was not selected.' } + Invoke-TestCase 'API JSON preserves the UTC update timestamp as a string' { + $fixtureRun = New-CiRun + $json = [pscustomobject]@{ total_count = 1; workflow_runs = @($fixtureRun) } | ConvertTo-Json -Depth 6 + $response = ConvertFrom-ReleaseApiJson -Json $json + $apiRun = @($response.workflow_runs)[0] + Assert-True ($apiRun.updated_at -is [string]) 'API JSON converted the update timestamp to a non-string value.' + Assert-Equal $apiRun.updated_at $fixtureRun.updated_at 'API JSON changed the exact UTC update timestamp.' + $selected = Select-ReleaseCiRun -CiRuns @($apiRun) -Repository $repository -CandidateCommit $candidateCommit + Assert-Equal $selected.id 73 'The parsed API run was rejected by the CI guard.' + } + Invoke-TestCase 'API JSON rejects malformed input' { + Assert-Throws { ConvertFrom-ReleaseApiJson -Json '{"workflow_runs": [broken}' } + } Invoke-TestCase 'main workflow path suffix' { $run = Select-ReleaseCiRun -CiRuns @((New-CiRun -Path '.github/workflows/ci.yml@refs/heads/main')) -Repository $repository -CandidateCommit $candidateCommit Assert-Equal $run.id 73 'The authorized workflow suffix was rejected.' From ae0d5488c1f6548334b8377b896905fa5971eee4 Mon Sep 17 00:00:00 2001 From: RomainROCH Date: Mon, 5 Oct 2026 08:34:28 +0200 Subject: [PATCH 12/15] docs: state supported release API parser hosts --- docs/RELEASING.md | 5 +++++ scripts/release-guard.ps1 | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/RELEASING.md b/docs/RELEASING.md index 7b24328..f9092df 100644 --- a/docs/RELEASING.md +++ b/docs/RELEASING.md @@ -43,6 +43,11 @@ only after it succeeds; there is no bypass or polling service. ## What the workflow does [release.yml](../.github/workflows/release.yml) uses two standard Windows jobs. +The release API driver supports Windows PowerShell 5.1 and PowerShell 7.5+; +PowerShell 6 through 7.4 are not supported by that driver and fail closed on +converted timestamps. The newer host uses `ConvertFrom-Json -DateKind String` +to preserve the UTC evidence, as described in the +[PowerShell reference](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/convertfrom-json#-datekind). The validation job has only `contents: read` and `actions: read`. It validates provenance and versions, runs Full checks, developer fixtures, smoke safety and release guards, then the state-changing Windows smoke in the disposable hosted diff --git a/scripts/release-guard.ps1 b/scripts/release-guard.ps1 index 95e2054..d386db2 100644 --- a/scripts/release-guard.ps1 +++ b/scripts/release-guard.ps1 @@ -3,8 +3,8 @@ Set-StrictMode -Version Latest function ConvertFrom-ReleaseApiJson { param([Parameter(Mandatory = $true)][string] $Json) - # PowerShell 7.5+ otherwise converts ISO dates to DateTime. Keep the GitHub - # timestamp text intact for the strict UTC check and rerun comparison. + # Supported API hosts: Windows PowerShell 5.1 or PowerShell 7.5+. The latter + # needs DateKind to keep timestamp text for the strict UTC/rerun checks. $jsonOptions = @{} if ((Get-Command ConvertFrom-Json).Parameters.ContainsKey('DateKind')) { $jsonOptions.DateKind = 'String' From 40da2eaa678a3d309a998f02912775dd31287f4e Mon Sep 17 00:00:00 2001 From: RomainROCH Date: Mon, 5 Oct 2026 08:41:04 +0200 Subject: [PATCH 13/15] fix: verify fixed PE version components during packaging --- docs/RELEASING.md | 3 +++ scripts/release-candidate.ps1 | 3 +-- scripts/release-guard.ps1 | 16 +++++++++++++++ tests/release_guards.ps1 | 38 +++++++++++++++++++++++++++++++++++ 4 files changed, 58 insertions(+), 2 deletions(-) diff --git a/docs/RELEASING.md b/docs/RELEASING.md index f9092df..e862306 100644 --- a/docs/RELEASING.md +++ b/docs/RELEASING.md @@ -58,6 +58,9 @@ The hash is taken before smoke and must match afterward. Package checks verify help/version, the real embedded manifest resource, Windows file version and MSVC host. They copy that binary without rebuilding it and produce: +The Windows version check reads the fixed four-component PE version resource; +its display string can omit the trailing `.0` without changing that resource. + - `wardoff.exe`: the exact unsigned checked binary; - `SHA256SUMS.txt`: its SHA-256; - `build-info.json`: tag, full source SHA, main CI run/attempt, build run URL, diff --git a/scripts/release-candidate.ps1 b/scripts/release-candidate.ps1 index 946174f..154831e 100644 --- a/scripts/release-candidate.ps1 +++ b/scripts/release-candidate.ps1 @@ -89,8 +89,7 @@ if ($identity.Count -ne 1 -or $identity[0].GetAttribute('version') -cne ($versio $level.Count -ne 1 -or $level[0].GetAttribute('level') -cne 'asInvoker' -or $level[0].GetAttribute('uiAccess') -cne 'false') { throw 'Embedded release manifest differs from the source contract.' } -$fileVersion = (Get-Item -LiteralPath $binary).VersionInfo.FileVersion -if ($fileVersion -cne ($version + '.0')) { throw 'Windows file version differs from the source contract.' } +$null = Assert-ReleaseWindowsVersion -FileVersionInfo (Get-Item -LiteralPath $binary).VersionInfo -Version $version $rustVersion = (& rustc -Vv) -join "`n" if ($LASTEXITCODE -ne 0 -or $rustVersion -notmatch 'host: x86_64-pc-windows-msvc') { throw 'Release must use the MSVC host toolchain.' } if (Test-Path -LiteralPath $PackageDirectory) { throw 'Package destination already exists.' } diff --git a/scripts/release-guard.ps1 b/scripts/release-guard.ps1 index d386db2..6a18c2a 100644 --- a/scripts/release-guard.ps1 +++ b/scripts/release-guard.ps1 @@ -17,6 +17,22 @@ function Test-ReleasePositiveInteger { return (($Value -is [int] -or $Value -is [long] -or $Value -is [uint32] -or $Value -is [uint64]) -and $Value -gt 0) } +function Assert-ReleaseWindowsVersion { + param([object] $FileVersionInfo, [string] $Version) + # FileVersion is a display string (winres emits X.Y.Z). The fixed PE + # version resource has four numeric components and is the version contract. + $parts = @($FileVersionInfo.FileMajorPart, $FileVersionInfo.FileMinorPart, + $FileVersionInfo.FileBuildPart, $FileVersionInfo.FilePrivatePart) + foreach ($part in $parts) { + if ($part -isnot [int] -or $part -lt 0 -or $part -gt 65535) { + throw 'Windows file version component is invalid.' + } + } + $numericVersion = $parts -join '.' + if ($numericVersion -cne ($Version + '.0')) { throw 'Windows file version differs from the source contract.' } + return $numericVersion +} + function Get-ReleaseVersion { param([string] $RepoRoot, [string] $TagName) if ($TagName -cnotmatch '^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$') { diff --git a/tests/release_guards.ps1 b/tests/release_guards.ps1 index 569060e..20cd0f2 100644 --- a/tests/release_guards.ps1 +++ b/tests/release_guards.ps1 @@ -101,6 +101,14 @@ function New-CiRun { } } +function New-WindowsVersionInfo { + param([string]$DisplayVersion = '0.2.1') + return [pscustomobject]@{ + FileVersion = $DisplayVersion + FileMajorPart = 0; FileMinorPart = 2; FileBuildPart = 1; FilePrivatePart = 0 + } +} + function New-CiJob { param([long]$RunId = 73) return [pscustomobject]@{ @@ -205,6 +213,36 @@ try { Set-Content -LiteralPath (Join-Path $repoFixture 'wardoff.manifest') -Encoding utf8 -Value '' Assert-Throws { Get-ReleaseVersion -RepoRoot $repoFixture -TagName $tagName } } + foreach ($displayVersion in @('0.2.1', '0.2.1.0')) { + Invoke-TestCase "numeric Windows version accepts display [$displayVersion]" { + $info = New-WindowsVersionInfo -DisplayVersion $displayVersion + $numericVersion = Assert-ReleaseWindowsVersion -FileVersionInfo $info -Version '0.2.1' + Assert-Equal $numericVersion '0.2.1.0' 'The numeric Windows version was not returned.' + } + } + foreach ($fieldName in @('FileMajorPart', 'FileMinorPart', 'FileBuildPart', 'FilePrivatePart')) { + Invoke-TestCase "numeric Windows version rejects mismatched $fieldName" { + $info = New-WindowsVersionInfo + $info.$fieldName++ + Assert-Throws { Assert-ReleaseWindowsVersion -FileVersionInfo $info -Version '0.2.1' } + } + foreach ($invalidPart in @($true, 'correct value as string', -1, 65536)) { + Invoke-TestCase "numeric Windows version rejects $fieldName invalid value [$invalidPart]" { + $info = New-WindowsVersionInfo + if ($invalidPart -is [string] -and $invalidPart -ceq 'correct value as string') { + $info.$fieldName = [string]$info.$fieldName + } else { + $info.$fieldName = $invalidPart + } + Assert-Throws { Assert-ReleaseWindowsVersion -FileVersionInfo $info -Version '0.2.1' } + } + } + Invoke-TestCase "numeric Windows version rejects missing $fieldName" { + $info = New-WindowsVersionInfo + $info.PSObject.Properties.Remove($fieldName) + Assert-Throws { Assert-ReleaseWindowsVersion -FileVersionInfo $info -Version '0.2.1' } + } + } Write-VersionFixture Invoke-TestCase 'lightweight remote tag' { From 12b61c902a88ff0eeb871715daf9a0fced14f6dd Mon Sep 17 00:00:00 2001 From: RomainROCH Date: Mon, 5 Oct 2026 21:43:00 +0200 Subject: [PATCH 14/15] fix: use fixed chunks for UTF-16 properties --- src/blocker/local.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/blocker/local.rs b/src/blocker/local.rs index 9454168..a6105a9 100644 --- a/src/blocker/local.rs +++ b/src/blocker/local.rs @@ -479,7 +479,9 @@ fn read_unicode_property(event_record: &EVENT_RECORD, property_name: &str) -> Op } let utf16 = property_buffer - .chunks_exact(2) + .as_chunks::<2>() + .0 + .iter() .map(|chunk| u16::from_ne_bytes([chunk[0], chunk[1]])) .take_while(|value| *value != 0) .collect::>(); From 921755e98256c0e51c2ce851f775323f1bcda18e Mon Sep 17 00:00:00 2001 From: RomainROCH Date: Mon, 5 Oct 2026 21:59:00 +0200 Subject: [PATCH 15/15] fix: preserve empty process inventories in smoke preflight --- tests/smoke_safety.ps1 | 61 ++++++++++++++++++++++++++++++++++++++++++ tests/smoke_test.ps1 | 2 +- 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/tests/smoke_safety.ps1 b/tests/smoke_safety.ps1 index 5a27bcd..afa9f37 100644 --- a/tests/smoke_safety.ps1 +++ b/tests/smoke_safety.ps1 @@ -33,6 +33,67 @@ try { try { Assert-Throws {} 'canary' } catch { $canaryCaught = $true } Assert-True $canaryCaught 'Assert-Throws accepted an action that did not throw.' + & { + # Load only these function definitions; never execute the smoke runtime. + $smokePath = Join-Path $PSScriptRoot 'smoke_test.ps1' + $parseErrors = $null + $smokeAst = [System.Management.Automation.Language.Parser]::ParseFile($smokePath, [ref]$null, [ref]$parseErrors) + if ($parseErrors) { throw (($parseErrors | ForEach-Object { $_.Message }) -join [Environment]::NewLine) } + foreach ($functionName in @('Get-SmokePreflightWardoffProcesses', 'Assert-SmokePreflight')) { + $definitions = @($smokeAst.FindAll({ + param($node) + $node -is [System.Management.Automation.Language.FunctionDefinitionAst] -and $node.Name -ceq $functionName + }, $true)) + Assert-True ($definitions.Count -eq 1) "Expected exactly one smoke function named $functionName." + . ([scriptblock]::Create($definitions[0].Extent.Text)) + } + + $preflightState = @{ Processes = @(); ProcessError = $false; TaskExists = $false; TaskError = $false; TaskCalls = 0 } + function Get-WardoffProcessesByName { + if ($preflightState.ProcessError) { throw 'Fixture process enumeration failed.' } + return @($preflightState.Processes) + } + function Get-SmokeCurrentSessionId { return 7 } + function Get-SmokeAutostartTaskExists { + $preflightState.TaskCalls++ + if ($preflightState.TaskError) { throw 'Fixture task query failed.' } + return $preflightState.TaskExists + } + $script:OwnedProcesses = [System.Collections.Generic.List[object]]::new() + $script:OwnedProcesses.Add([pscustomobject]@{ Id = 901; HasExited = $false }) + + Assert-SmokePreflight + Assert-True ($preflightState.TaskCalls -eq 1) 'An empty process inventory did not reach the fake task probe.' + + $preflightState.ProcessError = $true + Assert-Throws { Assert-SmokePreflight } 'could not query Wardoff processes before mutation' + Assert-True ($preflightState.TaskCalls -eq 1) 'A failed process query reached the task probe.' + $preflightState.ProcessError = $false + + $preflightState.Processes = @([pscustomobject]@{ ProcessId = 902; SessionId = 7; ExecutablePath = 'C:\other\wardoff.exe'; CreationDate = $null }) + Assert-Throws { Assert-SmokePreflight } 'conflicting Wardoff runtime process' + Assert-True ($preflightState.TaskCalls -eq 1) 'A conflicting process reached the task probe.' + + $preflightState.Processes = @([pscustomobject]@{ ProcessId = 901; SessionId = 7; ExecutablePath = $binaryPath; CreationDate = $null }) + Assert-SmokePreflight + Assert-True ($preflightState.TaskCalls -eq 2) 'The fake owned process did not pass the real preflight.' + + $preflightState.Processes = @() + $preflightState.TaskExists = $true + Assert-Throws { Assert-SmokePreflight } 'autostart task already exists' + Assert-True ($preflightState.TaskCalls -eq 3) 'The fake existing task was not checked.' + + $preflightState.TaskExists = $false + $preflightState.TaskError = $true + Assert-Throws { Assert-SmokePreflight } 'Fixture task query failed' + Assert-True ($preflightState.TaskCalls -eq 4) 'The fake task failure was not checked.' + + Assert-Throws { + Assert-SmokePreconditions -WardoffProcesses $null -CurrentSessionId 7 -BinaryPath $binaryPath -AutostartTaskProbe { return $false } + } 'WardoffProcesses' + Write-Output 'Smoke preflight fixtures passed (7 cases; no runtime or task access).' + } + $buildFixture = Join-Path ([IO.Path]::GetTempPath()) ('wardoff-smoke-build-' + [Guid]::NewGuid().ToString('N') + '.exe') try { [IO.File]::WriteAllBytes($buildFixture, [byte[]](1, 2, 3)) diff --git a/tests/smoke_test.ps1 b/tests/smoke_test.ps1 index 0ca777a..36c73dd 100644 --- a/tests/smoke_test.ps1 +++ b/tests/smoke_test.ps1 @@ -532,7 +532,7 @@ function Test-SmokeOwnedAutostartTask { } function Assert-SmokePreflight { - $processes = Get-SmokePreflightWardoffProcesses + $processes = @(Get-SmokePreflightWardoffProcesses) Assert-SmokePreconditions ` -WardoffProcesses $processes ` -CurrentSessionId (Get-SmokeCurrentSessionId) `