Skip to content

fanboykun/watcher

Repository files navigation

Watcher Agent

Watcher

Go Version License: MIT Total downloads

Watcher is a pull-based deployment agent for Windows machines.

It runs as a single binary (watcher.exe) that hosts:

  • a background deployment agent
  • a REST API
  • an embedded Svelte dashboard

The agent polls GitHub releases, deploys new artifacts, manages services, performs health checks, and records state/history in SQLite.

Download Watcher


Install

Download the latest release zip, extract it on the Windows host, then run install.bat to start the interactive installer.

Full installation guide: INSTALL.md


What It Does

  • Pull-based deploys from GitHub Releases (no inbound SSH required)
  • Multiple watchers (one poll loop per watcher)
  • Service types:
    • nssm binaries (managed start/stop/restart)
    • static apps (IIS app pool recycle support)
  • Automatic rollback on failed post-deploy health checks
  • Manual rollback with high-watermark pinning (max_ignored_version)
  • Poll-event history (new_release, no_update, skipped, error, etc.)
  • Optional GitHub Deployment API status reporting
  • Per-watcher GitHub override settings:
    • deployment_environment (fallback: global ENVIRONMENT)
    • github_token (fallback: global GITHUB_TOKEN)
  • Per-watcher outbound webhooks with durable delivery history
  • Self-management endpoints (version, update-check, update, uninstall script)
  • Embedded Svelte SPA dashboard served by the same Go process

Runtime Architecture

cmd/watcher/main.go
  ├─ load .env config (Viper)
  ├─ open SQLite (GORM + glebarez/sqlite)
  ├─ start Gin API server
  └─ run Agent
       ├─ sync watchers from DB
       ├─ one goroutine per watcher
       ├─ immediate check trigger channel (API -> agent)
       └─ sync trigger channel (API -> agent)

Each watcher loop:

  1. Fetch release metadata from GitHub
  2. Compare remote version vs current_version
  3. Enforce rollback high-watermark (max_ignored_version)
  4. Deploy if needed:
    • download artifact (retry with backoff)
    • extract to releases/<version>
    • stop services
    • swap current junction (mklink /J, copy fallback)
    • ensure service registration (NSSM for nssm type)
    • start services / recycle IIS app pools
    • health checks
    • rollback on failure
  5. Persist state and deploy logs in SQLite

Metadata Sources

Watcher prefers a version.json manifest. Repo asset discovery still exists as a legacy/manual fallback, but new org release workflows should publish version.json for every Watcher-managed release.

metadata_url accepts either:

  • a direct release manifest URL such as https://github.com/<owner>/<repo>/releases/latest/download/version.json, or
  • a repo URL such as https://github.com/<owner>/<repo>. During watcher creation, the UI tries to find version.json for the selected release ref first.

Detailed manifest reference: docs/version-json.md.

Consumer release requirements

Watcher only picks up a consumer repository release when the app repository follows these rules:

  • Publish a GitHub Release, not only a tag. Draft releases are ignored by GitHub's latest-release API.
  • Upload the deployable zip as a release asset.
  • Upload version.json as a release asset for every Watcher-managed release. This is the hard org contract.
  • Keep version.json.services["<service_name>"] exactly equal to the Watcher service_name.
  • Set version to the exact version Watcher should compare. For monorepos, prefer the full service tag such as services/prs/v1.2.3.
  • Set artifact to the exact release asset filename.
  • Set artifact_url to the exact GitHub release download URL for that asset. Slash tags such as services/prs/v1.2.3 are supported.
  • Set deployment hints (app_kind, binary_name, IIS fields, health URL) so the create wizard can build the right service config without guessing.
  • Package the zip so Watcher can use it immediately after extraction:
    • nssm: the configured binary_name must exist at the zip root.
    • static / php / aspnet_classic: the site/app files must exist at the zip root.
  • For monorepos, publish a manifest index release, usually tag latest, that points each service to its service-scoped release asset.
  • Do not publish two service releases concurrently from the same repo; serialize release workflows with GitHub Actions concurrency.

version.json shape

{
  "services": {
    "my-app": {
      "version": "v1.2.3",
      "artifact": "my-app-v1.2.3.zip",
      "artifact_url": "https://github.com/<owner>/<repo>/releases/download/v1.2.3/my-app-v1.2.3.zip",
      "published_at": "2026-06-01T04:19:05Z",
      "app_kind": "nssm",
      "windows_service_name": "my-app",
      "binary_name": "my-app.exe",
      "start_arguments": "",
      "env_file": ".env",
      "health_check_url": "http://localhost:8080/health"
    }
  }
}

Supported manifest properties

Property Required Applies to Meaning
version yes all Version stored in Watcher state and compared on each poll.
artifact yes all Exact release asset filename.
artifact_url yes all GitHub release download URL for the deployable artifact.
published_at recommended all UTC timestamp for display and diagnostics.
app_kind recommended all nssm, static, php, or aspnet_classic.
windows_service_name recommended all NSSM service name or IIS service identifier/default name.
binary_name required for nssm nssm Executable filename expected after extraction.
start_arguments optional nssm Arguments passed to the NSSM application.
env_file optional nssm Relative env file path for managed env content.
health_check_url optional all Service-level health check URL.
iis_app_pool recommended static, php, aspnet_classic IIS app pool name.
iis_site_name recommended static, php, aspnet_classic IIS site name.
iis_managed_runtime optional aspnet_classic IIS runtime such as v4.0; leave empty for static/PHP.
public_url optional IIS-hosted apps URL displayed in the UI and useful for health checks.

Artifact expectations

  • Zip artifacts should contain deploy binaries/content at the root.
  • For nssm services, binary_name must exist after extraction under current/.
  • For IIS-hosted services, site files should exist directly under current/.

API Summary

Base path: /api

System

  • GET /status
  • GET /logs
  • GET /logs/stream
  • POST /github/inspect

Watchers

  • GET /watchers
  • POST /watchers
  • GET /watchers/:id
  • PUT /watchers/:id
  • DELETE /watchers/:id
  • GET /watchers/:id/services
  • POST /watchers/:id/services
  • PUT /watchers/:id/services/:sid
  • DELETE /watchers/:id/services/:sid
  • GET /watchers/:id/deploys
  • GET /watchers/:id/deploys/:did
  • GET /watchers/:id/deploys/:did/stream
  • GET /watchers/:id/events
  • GET /watchers/:id/polls
  • POST /watchers/:id/check
  • POST /watchers/:id/redeploy
  • GET /watchers/:id/versions
  • POST /watchers/:id/rollback
  • POST /watchers/:id/resume
  • DELETE /watchers/:id/versions/:version
  • GET /watchers/:id/webhook-events
  • GET /watchers/:id/webhook-deliveries
  • GET /watchers/:id/webhook-deliveries/:deliveryId
  • POST /watchers/:id/webhook/test
  • POST /watchers/:id/webhook/resume

Services (flat)

  • GET /services
  • GET /services/:id
  • POST /services/:id/start
  • POST /services/:id/stop
  • POST /services/:id/restart
  • PUT /services/:id/env
  • GET /services/:id/health
  • GET /services/:id/health/history
  • GET /services/:id/logs
  • GET /services/:id/deploys

Self

  • GET /self/version
  • GET /self/config
  • PUT /self/config
  • GET /self/update-check
  • POST /self/update
  • POST /self/restart
  • POST /self/uninstall

Dashboard Routes

  • / Dashboard
  • /watchers
  • /watchers/:id
  • /services
  • /services/:id
  • /polling
  • /logs
  • /settings

Config (.env)

Example is in .env.example.

ENVIRONMENT=production
GITHUB_TOKEN=
LOG_DIR=D:\apps\watcher\logs
NSSM_PATH=C:\ProgramData\chocolatey\bin\nssm.exe
DB_PATH=D:\apps\watcher\watcher.db
API_PORT=8080
API_BASE_URL=
WATCHER_REPO_URL=https://github.com/fanboykun/watcher

Notes:

  • GITHUB_TOKEN is required for private repos.
  • API_BASE_URL enables GitHub Deployment API log_url linking.
  • WATCHER_REPO_URL is used by self-update check/update.
  • GITHUB_DEPLOY_ENABLED=true|false toggles GitHub Deployment API reporting globally.
  • Watcher-level config can override deploy environment/token per repo:
    • deployment_environment -> used first for GitHub Deployments environment
    • github_token -> used first for GitHub metadata/artifact/deployment API calls
    • if empty, watcher falls back to global .env values.

Webhooks

Watcher can emit durable outbound webhook events for:

  • watcher.version_found
  • watcher.deployment_succeeded
  • watcher.deployment_failed
  • watcher.rollback_succeeded
  • watcher.rollback_failed
  • service.health_changed
  • watcher.webhook_test
  • webhook.delivery_exhausted

Important behavior:

  • Delivery is at-least-once. Deduplicate on event_id.
  • Each HTTP attempt gets its own delivery_id.
  • Any 2xx counts as success.
  • Network errors, 429, and 5xx retry.
  • Other 4xx responses are recorded as final failures.
  • Watcher preserves FIFO delivery order per watcher.
  • Paused webhook delivery suppresses new events instead of dropping them.

Detailed contract, event payload fields, and trigger rules:

For local testing, this repo also includes a minimal receiver:

go run ./cmd/webhook-server -addr :8091 -path /webhook

GitHub Token Guide

Use a token if:

  • repo is private, or
  • you use GitHub Deployment API status reporting.

Option A — Fine-grained PAT (recommended)

  1. GitHub → SettingsDeveloper settingsPersonal access tokensFine-grained tokens
  2. Select repository access for the repos watcher needs.
  3. Grant minimum permissions:
    • Contents: Read (required for release metadata/assets)
    • Deployments: Read and write (required only if using GitHub Deployment API status updates)
  4. Generate token and store it in:
    • global .env as GITHUB_TOKEN, or
    • watcher override in UI (Watchers → Add/Edit watcher).

Option B — Classic PAT

  • For private repos, repo scope is typically sufficient.
  • If deployment status calls are used, ensure deployment-related repo operations are allowed by org/repo policy.

Common 404 causes during inspect/deploy

  • no published release exists (/releases/latest needs a published, non-draft release),
  • token does not have access to the target private repo,
  • wrong owner/repo URL,
  • org SSO/policy not approved for the token.

Data Model

Tables auto-migrated on startup:

  • watchers
  • services
  • deploy_logs
  • health_events
  • poll_events

Highlights:

  • Watcher state fields: current_version, max_ignored_version, status, last_checked, last_deployed, last_error
  • Service fields include service_type, binary_name, env_file, health_check_url, iis_app_pool, iis_site_name, public_url, env_content
  • Deploy log includes github_deployment_id and raw text logs

Build and Dev

Requirements

  • Go 1.25.x (module currently uses go 1.25.6)
  • Bun (for web build)

Common commands

make help
make dev
make build-web
make build
make test
make test-verbose
make test-github
make run
make package
make clean

make build builds the web app first, then embeds web/build into the Go binary.


CI / Release Files

  • .github/workflows/release.yml
    • releases the watcher itself
  • workflows/release-go-nssm.yml
    • Watcher-ready release template for a single Go repository with one or more Windows/NSSM binaries
    • emits a flat Windows amd64 zip and version.json
  • workflows/release-bun-iis.yml
    • Watcher-ready release template for a SvelteKit static site served as a static/IIS target
    • emits a static build zip and version.json
  • workflows/release-go-monorepo.yml
    • Watcher-ready release template for one Go service inside a mono repo
    • uses service-scoped tags such as <service>/v1.2.3 and service-only path triggers
  • workflows/deploy.yml
    • legacy direct deployment workflow template

Install

See INSTALL.md.

Typical flow on Windows:

  1. Extract release zip
  2. Run install.bat
  3. Complete wizard (shell/install-watcher.ps1)
  4. Open dashboard on http://localhost:<API_PORT>

Important Operational Notes

  • API has no built-in auth; deploy behind a trusted network.
  • service_name must match metadata service key for version.json flow.
  • binary_name must match the extracted file for nssm services.
  • Manual rollback sets max_ignored_version; auto-deploy ignores versions <= that value until resumed or a newer version appears.
  • After repeated failures for the same target version, auto deploy is suspended for that version until manual redeploy.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors