Some ISCC builds reject deeply-nested absolute .iss paths (the runner
runs out of %SystemRoot%\System32\config\systemprofile\.cache\act\...)
with a misleading 'The system cannot find the path specified.' before
touching any source files. Push-Location to the .iss directory and
pass the bare filename - same effect, no path quirk.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ISCC's default error message ("The system cannot find the path
specified.") doesn't say which file failed, which made the Gitea
runner failure undebugable. Two changes:
1. Before invoking ISCC, log a one-line summary per source path the
.iss references: file count, longest path length, root. Also dump
HKLM\...\FileSystem\LongPathsEnabled so MAX_PATH issues are
immediately obvious.
2. Pass /Qp to ISCC so it prints each file as it processes; the line
right before the error tells us which source file ISCC choked on.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PowerShell with ErrorActionPreference=Stop escalates ANY native-command
stderr output to a script-terminating error. git writes plenty of
informational lines to stderr (CRLF nags, "remote: Processed N
references", "Switched to branch X"), which made the sync script
abort partway through every run when actually nothing was wrong.
Three fixes:
1. Switch to ErrorActionPreference=Continue and check $LASTEXITCODE
manually after each git call.
2. Drain stderr on each git invocation with `2>&1 | Out-Null`.
3. Disable core.autocrlf and core.safecrlf in the throwaway wiki
clone so git stops complaining about line endings.
Verified end-to-end against Gitea: 12 pages + sidebar pushed cleanly.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
scripts/sync-wiki.ps1 clones a wiki repo, copies+flattens markdown
from docs/ with a slug mapping (e.g. recipes/zerto-pre-post-scripts.md
becomes the Recipe-Zerto-Failover page), rewrites in-repo markdown
links to wiki-style targets, generates a _Sidebar.md, and pushes back
if anything changed. Idempotent.
.github/workflows/wiki-sync.yml runs the sync on every push to main
that touches docs/ (or the sync tooling itself). Uses GITHUB_TOKEN
which has wiki write access via the contents:write permission.
For Gitea, no Windows runner is available, so the script is invoked
manually with a Gitea PAT in the URL. One-time setup for each remote:
enable Wiki in repo settings, create a Home page via the web UI to
initialize the wiki repo, then run the sync.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Documentation: install/upgrade/uninstall guides + recipes incl. Zerto
Adds a docs/ folder under the repo root with full operator documentation
aimed at sysadmins (not webhook developers). The Zerto pre/post script
recipe is the canonical "why does this exist" walkthrough; the GitHub
HMAC, AD password reset, and UI-on-desktop recipes round out common
patterns.
Pages:
- README.md (index)
- concepts.md (5-minute "what is a webhook" explainer)
- installation.md (interactive + silent install)
- upgrading.md (single-click upgrade flow + edge cases)
- uninstalling.md (clean removal + wiping ProgramData)
- runas-modes.md (Service / InteractiveUser / SpecificUser decision flow)
- service-account-and-ad.md (gMSA setup, delegated rights)
- network-and-security.md (bind addresses, allowlists, HTTPS, secret storage)
- troubleshooting.md (symptom -> first check, common errors)
- recipes/zerto-pre-post-scripts.md (canonical use case)
- recipes/github-style-hmac.md (GitHub / Stripe-shaped webhooks)
- recipes/ad-password-reset.md (gMSA-backed self-service reset)
- recipes/ui-on-desktop.md (InteractiveUser pattern)
Top-level README.md restructured to point at docs/ as the source of
truth, dropping the duplicated installation snippets.
Installer ships docs/ alongside the binaries so they're available
offline at C:\Program Files\WebhookServer\docs\. GUI Help menu gains
a "Documentation" item that opens the docs site in a browser.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Config Checkpoints dialog + daily auto-checkpoint; drop installer GUI launch
Three fixes:
1. Config Checkpoints submenu replaced with a proper dialog. Lists
checkpoints with timestamp/size/filename, has a "Take Checkpoint
Now" button, and a "Roll Back" button that becomes enabled when a
row is selected. The previous click-a-menu-entry-immediate-restore
flow was too easy to fire by accident.
2. New CheckpointScheduler BackgroundService creates a checkpoint at
midnight every day. Combined with the existing auto-on-save
snapshots, this guarantees a daily rollback point even if the
config wasn't edited that day. A new "create-checkpoint" admin op
plus AdminPipeServer.CreateCheckpoint helper does the actual file
copy; both manual (via the dialog) and the scheduler use it.
3. Installer: drop the post-install "Launch Webhook Server" wizard
step. It tried to launch the GUI un-elevated, which fails because
the GUI's manifest is requireAdministrator. The Start Menu shortcut
handles elevation correctly, so the user can launch from there.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Docs: replace AD-reset recipe with realistic Zerto failover walkthrough
The AD password reset endpoint was a poor fit for what people actually
need this server for. Replaced with a realistic Zerto post-failover
example that's much closer to the project's purpose:
- Update DNS A records for failed-over hostnames
- Wait for the VM to come up at the DR site
- PowerShell-remote into the VM and check / start critical services
- Notify Teams with the result
The flagship pattern is now: Zerto post-script (curl, fire-and-forget)
calls an Async webhook endpoint -> 202 in milliseconds -> Zerto's
failover sequence is never blocked. The server runs the actual work in
the background, with full output captured in the daily log.
A ready-to-use Zerto-side script ships at
scripts/examples/zerto-post-failover.ps1 - pure curl.exe (no
PowerShell modules), reads the bearer token from a file the ZVM
service account can read.
The installer now bundles scripts/examples/ alongside docs/ so the
example is also available locally at
C:\Program Files\WebhookServer\scripts\examples\.
Removed: docs/recipes/ad-password-reset.md.
Updated: docs/README.md, README.md, the recipe content itself.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Restore installer GUI launch (via shellexec) + checkpoint descriptions
Two follow-ups to the previous Config Checkpoints commit:
1. Bring back the post-install "Launch Webhook Server" checkbox in the
installer. The previous attempt failed because Inno Setup's
postinstall flag launches via CreateProcess after Setup exits,
bypassing the GUI's requireAdministrator manifest. Adding the
shellexec flag switches to ShellExecute, which DOES honor the
manifest and triggers a clean UAC prompt - so the post-install
GUI launch works as expected.
2. Each checkpoint now carries a description, stored in a sidecar
.meta.json file next to the snapshot. Defaults:
- Auto-on-save: "Before save"
- Midnight scheduler: "Nightly auto-checkpoint"
- Manual: opens a small dialog so the user can type a meaningful
description (defaults to "Manual checkpoint" if blank)
The dialog and pruning both clean up sidecars alongside snapshots.
The Config Checkpoints grid grows a Description column between
When and Size.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* v0.1.2: bump checkpoint retention 30 -> 90
Each checkpoint is a few KB of JSON plus a tiny sidecar; even at 90
entries on a config with hundreds of endpoints the on-disk footprint
is negligible (worst case ~20 MB). With daily auto-checkpoints plus
on-save snapshots, 30 entries could fill in a couple weeks of
moderate use; 90 gives a comfortable ~3-month window.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Phase 3: app icon (multi-resolution ICO + master PNG)
scripts/generate-icons.ps1 renders the icon programmatically with
System.Drawing - rounded teal square (#0E7C66) with a stylized white
hook glyph - at 16/24/32/48/64/128/256 px and assembles a proper
multi-resolution Microsoft ICO. The PNG and ICO outputs land in
resources/. The script is the source of truth; re-run after editing
the design.
GUI csproj uses ApplicationIcon for the EXE icon and embeds the .ico
+ .png as Resources so MainWindow and AboutDialog can use them via
WPF's resource URI scheme.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Phase 5: tray icon with minimize-to-tray and context menu
GUI csproj enables UseWindowsForms (NotifyIcon lives in WinForms even
in .NET 8). New Services/TrayIcon.cs wraps NotifyIcon with a context
menu (Open / Restart service / Exit) and the embedded webhook-server
icon. MainWindow creates the TrayIcon, hides itself on minimize and
restores on tray double-click.
Adds GlobalUsings.cs to alias the WPF defaults for types that exist
in both WPF and WinForms (Application, MessageBox, TextBox, Binding,
etc.) so existing code keeps compiling without per-file changes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Phase 6+7: Inno Setup installer + GitHub Actions release pipeline
installer/webhook-server.iss is an Inno Setup 6 script that:
- Installs to %ProgramFiles%\WebhookServer
- Creates Start Menu folder + GUI shortcut (and optional desktop icon)
- Runs install-service.ps1 post-install to register the Windows Service
- Runs uninstall-service.ps1 pre-uninstall to remove it
- Bundles the webhook-server icon for the installer / uninstaller
scripts/build-installer.ps1 is the local build helper: publishes both
projects, finds ISCC.exe (PATH or standard install path), compiles the
installer with the version pulled from Directory.Build.props, drops the
output in dist/.
.github/workflows/ci.yml runs build + test on every push/PR to main.
.github/workflows/release.yml triggers on v* tags (or manual dispatch),
runs tests, installs Inno Setup via choco, builds the installer, and
attaches the .exe to a GitHub Release. Pre-1.0 versions are flagged
prerelease automatically.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Phase 4: backups + import/export config
ConfigStore.SaveAsync now snapshots the previous config to
%ProgramData%\WebhookServer\backups\config-<timestamp>.json before
overwriting, retaining the last 30. Failures are silent so a
backup-write hiccup never blocks an actual save.
Three new admin pipe ops:
- list-backups: returns newest 50 entries with timestamps and sizes
- restore-backup: takes a fileName, refuses path-traversal chars,
loads the named backup over the live config (which itself triggers
a fresh backup of the current state via the SaveAsync hook)
- import-config: replaces the current config with a GUI-supplied
ServerConfig, merging encrypted secrets where the GUI didn't supply
new plaintext
GUI File menu items are wired:
- Import config: file picker -> ImportConfigAsync
- Export config: SaveFileDialog writes the current config as JSON
- Backups: dynamic submenu auto-refreshed when opened, listing
backups with timestamp + size; click to confirm-and-restore
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PowerShell 5.1 reads .ps1 files as the local ANSI codepage by default,
so non-ASCII characters get garbled. An em-dash inside a string literal
broke install-service.ps1 with a parser error. Sticking to ASCII in
script source avoids the entire class of issue.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
sc.exe query writes "The specified service does not exist" to stdout
when the service is missing, so checking truthy on its output was
useless — it always took the update branch and silently failed when
piped to Out-Null. Switch to Get-Service which returns $null cleanly,
and stop swallowing sc.exe output so missing-service / permission /
account errors actually surface as PowerShell errors.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
GUI:
- URL column in endpoint grid + Copy URL toolbar button so the full
http://host:port/hook/<slug> is one click away
- Double-click a row to open the edit dialog
- Bearer/HMAC sections in the editor hide when the auth mode doesn't
use them, and reappear with previously-entered values when switched
back
- Log panel auto-scroll checkbox (default on) plus 3s polling so log
entries stream in without manual refresh
- Secret fields are now plain text with a Copy button. Anyone who can
open the admin-pipe-ACL'd GUI is already SYSTEM-equivalent on the
host, so masking the value just made recovery harder. PFX password
in Server Settings gets the same treatment.
Service:
- Admin pipe ops log info-level lines on every mutation
(create/update/delete/enable/disable/update-config/bind-https) so
GUI activity is visible in the Serilog file
- /hook/{slug} accepts GET as well as POST so a browser smoke-test
works without curl
- /favicon.ico returns 204 so browser hits don't pollute logs with 404s
- AdminPipeServer no longer strips plaintext secrets when sending
config to the GUI; the pipe ACL already restricts to SYSTEM/Admins
Scripts:
- New deploy.ps1: stops + republishes + copies binaries to
C:\Program Files\WebhookServer + (re)installs the Windows Service
- install-service.ps1 now uses sc.exe argv splatting consistently for
both create and config paths
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Stands up an isolated data root, seeds a single sample endpoint, opens
the service in its own window, then launches the GUI with the matching
WEBHOOKSERVER_DATA environment variable. Refuses to run from a
non-elevated shell since the admin pipe is ACL'd to SYSTEM and
Administrators only.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add the .NET 8 solution scaffolded against PLAN.md. Three projects share
WebhookServer.Core (models, auth, execution, storage, IPC, callbacks)
and WebhookServer.Service hosts an embedded Kestrel listener plus the
named-pipe admin server. WebhookServer.Gui is a thin MVVM client over
the pipe. Includes 25 unit tests covering HMAC verification, bearer
auth, IP allowlist parsing, arg-template rendering, DPAPI round-trip,
and the encrypt-on-save config store.
Install/uninstall PowerShell scripts default to LocalSystem and accept
a domain user or gMSA via -ServiceAccount.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>