* 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>
5.8 KiB
Run As modes — when to use which
Each endpoint has a Run As setting (in the editor's "Run as" section) that controls who the script runs as. The default works for most cases, and switching modes is one dropdown change.
The three modes
| Mode | Runs as | Use when… |
|---|---|---|
| Service (default) | Whoever the Windows Service runs under (LocalSystem by default) | Almost everything. Local file ops, calling local APIs, running cmd / PowerShell scripts that don't need a user identity. |
| InteractiveUser | The user logged in at the keyboard | The script needs to put a window on the screen (Calculator, a notification dialog, opening a browser tab) |
| SpecificUser | A named local or domain user / password you provide | The script runs in AD, a fileshare, or any system that wants the action attributed to a specific identity — and you don't want the service itself running as that user. |
Service (default)
Nothing to configure. The hook runs as LocalSystem by default — full local rights, very limited network identity (the machine account on a domain).
You can change the service identity at install time via the -ServiceAccount parameter to install-service.ps1 (gMSA, domain user, etc.). Anything you set there applies to all Service-mode endpoints. See Service account & Active Directory.
Pros: zero config per endpoint, no passwords to manage, fastest path Cons: the script can't pop UI on the user's desktop (Session 0 isolation), and on a workgroup machine it has no domain identity at all
InteractiveUser
Pick this when the hook should appear visually on the desktop of whoever is logged in. The clearest example is "fire a hook from my phone, get a Calculator window on my PC."
How it works internally: the service (running as SYSTEM) calls the Win32 API WTSQueryUserToken to grab the active console session's user token, then CreateProcessAsUser to land the new process inside that session.
What you don't have to configure: username, password, profile loading, session ID. All inferred at runtime.
What can go wrong:
- No one logged in at the keyboard → hook fails with
No active console session - is anyone logged in at the keyboard?. The hook can't run; there's no desktop to land on. - Service runs as anything other than LocalSystem →
WTSQueryUserTokenrequires SYSTEM. If you switched the service to a gMSA / domain user, InteractiveUser stops working. - Locked desktop, no user logged in but session 1 reserved → similar to "no one logged in." Once a user logs in interactively (even just to the lock screen with credentials cached), the session is "active enough" for this to work.
Use case examples: see recipes/ui-on-desktop.md.
SpecificUser
Pick this when the hook needs to authenticate as a specific account — a service account with delegated AD rights, a local Administrator on a remote machine, etc. — but you don't want the whole service running as that account.
Configure:
- Username:
DOMAIN\user,.\local-user, or a UPN likeuser@contoso.com. The leading.\is shorthand for the local machine. - Password: stored DPAPI-encrypted at rest. Visible in plaintext in the GUI for an admin user, by design — anyone with admin pipe access already has SYSTEM-equivalent rights.
- Load profile: optional. Loads the user's HKCU and AppData before running. Slower (~1s extra). Only needed if the script reads user-scoped settings (uncommon).
How it works internally: the service calls LogonUser with the credentials (interactive logon type first, falls back to batch logon for service-only accounts), then DuplicateTokenEx + CreateProcessAsUser. The script lands in a fresh batch session with the user's network identity.
Why not
psi.UserName/psi.Passwordlike a normal .NET app? BecauseCreateProcessWithLogonW(what those properties use under the hood) refuses to run when the caller isLocalSystem, which is exactly our scenario. The token-based path is the documented Windows mechanism for this.
What can go wrong:
- Wrong password → log shows
LogonUser (DOMAIN\user) failed - The user name or password is incorrect. Re-enter in the editor. - Account is denied logon locally → log shows
Logon failure: the user has not been granted the requested logon type. Make sure the account has at least one of Log on as a batch job or Log on locally undersecpol.msc→ Local Policies → User Rights Assignment. - Domain controller unreachable → for domain accounts, the service must be able to reach a DC. For local accounts (
.\name), no domain dependency.
Decision flowchart
Need UI on the user's desktop?
│
┌─────── yes ─────┴────── no ─────┐
│ │
InteractiveUser Need specific identity (AD / fileshare / etc.)?
│
┌──── yes ────┴──── no ────┐
│ │
Should ALL hooks run as Service
this identity?
│
┌────── yes ──────────┴───────── no ──────────┐
│ │
Run service itself SpecificUser per endpoint
as that account
(gMSA / domain user)
see service-account-and-ad.md