Skip to content

Command Palette

Search for a command to run...

Deployments

Git webhook deploys

Deploy the exact pushed commit through the existing rebuild pipeline, with durable jobs and recovery.

The deployment pipeline

phelix webhook accepts Git push webhooks and deploys the exact pushed commit, never the branch's current tip or the server's working tree. It only orchestrates the trigger: HMAC verification, branch validation, delivery deduplication, a per-app queue, and isolated source preparation precede the existing phelix rebuild pipeline.

Classic, blue-green, rolling, canary, and progressive strategies come from phelix.yaml. Health verification, versioning, rollback history, build reports, and the per-app deploy lock remain owned by the rebuild pipeline. Every accepted delivery becomes a durable deployment job.

Configuration

Enable webhooks in each application's project configuration:

phelix.yamlyaml
webhook:  enabled: true  branch: main  secret_env: PHELIX_WEBHOOK_SECRET
FieldMeaning
webhook.enabledDefaults to false. Apps without a webhook section never accept deliveries.
webhook.branchRequired when enabled. Bare branch name, such as main or release/2.x, not refs/heads/main.
webhook.secret_envRequired when enabled. Name of the environment variable holding the HMAC-SHA256 shared secret.

Running the server

Terminal
# Supply PHELIX_WEBHOOK_SECRET through the server environment firstphelix webhook                              # 127.0.0.1:9746phelix webhook --host 0.0.0.0 --port 9746     # explicitly listen on all interfaces

The default listener is 127.0.0.1:9746. The server runs in the foreground and exits cleanly on SIGINT or SIGTERM. Supervise it with systemd, supplying the secret through Environment= or an EnvironmentFile. Apps are resolved live from apps.json, so additions and removals need no restart.

Endpoint and responses

Terminalhttp
POST /webhook/<app>

The app can be named by name or ID. GitHub-style headers are required:

HeaderMeaning
X-Hub-Signature-256: sha256=<hex>HMAC-SHA256 of the exact raw request body, compared in constant time.
X-GitHub-Delivery: <id>Unique delivery ID used for replay protection.
X-GitHub-Event: pushEvent type. Ping and other non-push events are acknowledged but ignored.

Only ref and after (the pushed commit SHA) are read from the provider's push payload. Refs such as refs/heads/main normalize to main. Branch mismatch, branch deletion, and non-push events return success without queueing a rebuild.

Responses are JSON with accepted, queued, and message fields. Internal errors, paths, and secrets are never exposed.

Accepted deliveryjson
{"accepted":true,"queued":true,"message":"webhook accepted"}
SituationHTTP statusResult
Accepted and queued200accepted: true, queued: true
Branch mismatch, deletion, or non-push event200accepted: true, queued: false
Duplicate delivery200accepted: true, queued: false; delivery already processed
Invalid, missing, or malformed signature401invalid signature
Unknown app or disabled webhook404unknown application
Unreadable payload or missing delivery ID400invalid payload
Body over 2 MiB413request body too large
Queue full or unavailable503webhook queue unavailable; provider can redeliver

Error responses set both accepted and queued to false.

Replay protection and queue

  • Accepted delivery IDs and their exact commit SHAs are persisted before acknowledgement in ~/.phelix/webhook/deliveries.json. The ledger retains the 512 most recent deliveries and prevents a second rebuild for duplicates within that window, including concurrent requests and restarts.
  • The response returns after queueing, not after the fetch or rebuild. Jobs for one app run serially; different apps run independently. Accepted jobs are not coalesced and keep their own commit.
  • Webhook jobs wait for a manual rebuild or rollback to release the app's deploy lock. They never steal or bypass it; the rebuild pipeline remains responsible for acquiring and releasing it.
  • Shutdown gives an in-flight job up to 60 seconds to finish. An already-running rebuild subprocess is left to complete independently; its source is not removed underneath it. Queued, unstarted jobs are dropped and logged.

Exact-commit source

The managed app must be inside a Git repository with a configured remote, usually origin. Repository paths come from the app's source directory, never the webhook payload. Git uses that repository's configured credentials; the webhook does not manage or store Git credentials.

  1. Validate the pushed SHA as 40 or 64 hexadecimal characters. Git receives structured arguments, not shell interpolation.
  2. Fetch the configured branch without pulling or moving the working tree. Verify the exact commit exists, retrying a direct SHA fetch when the remote permits it.
  3. Create a fresh detached Git worktree under ~/.phelix/webhook/worktrees/. The application's branch, HEAD, and uncommitted changes remain untouched. Monorepo apps use the same subdirectory in the worktree.
  4. Verify the worktree HEAD matches the pushed SHA, then run phelix rebuild <app> --source-dir <isolated source>.

App identity, output binary, ports, and deployment state stay with the managed app. Configuration and recorded Git commit come from the isolated source. Git preparation failures stop the job before any rebuild: Phelix never falls back to whatever happens to be on disk.

Worktrees are removed after success or failure. Cleanup errors are logged without masking deployment results. Startup sweeps abandoned worktrees after a one-hour grace period so orphaned rebuilds can finish.

Deployment jobs

Every accepted delivery has an atomic JSON record under ~/.phelix/webhook/jobs/ before it is acknowledged. Records include job and delivery IDs, app, branch, exact commit, status, stage, timestamps, resulting version, and classified errors. They exclude secrets, request bodies, and temporary worktree paths.

Job lifecycle
accepted → queued → syncing → building → deploying → health_checking → succeededTerminal alternatives: failed, rolled_back, cancelledStages: queue, git_sync, build, deploy, health_check, cleanup, recovery

Failure codes include GIT_SYNC_FAILED, BUILD_FAILED, DEPLOY_FAILED, HEALTH_CHECK_FAILED, AUTO_ROLLBACK_FAILED, and WEBHOOK_QUEUE_FAILED. Automatic recovery is recorded as rolled_back with the restored version, based on the rebuild pipeline's rollback history. Successful jobs record the version created for their exact commit.

Terminal
phelix webhook status api            # in-flight jobs and live stagesphelix webhook history api           # recent finished jobsphelix webhook history api --limit 50phelix webhook status api --json      # machine-readable records

These inspection commands are read-only, work offline, and require no authentication.

Restart recovery

Startup reconciles unfinished jobs without re-running them. If a rebuild subprocess had started and the app's current version matches the exact pushed commit and was promoted after acceptance, the job is marked succeeded with that version. Otherwise it is marked failed with WEBHOOK_JOB_INTERRUPTED. Dropped, merely queued jobs are cancelled.

History keeps the 200 most recent finished jobs; active jobs are never deleted. This is separate from the 512-entry delivery ledger, so trimming job history does not weaken replay protection.

Events and monitoring

Webhook events use the existing application event channel, subject to per-app watching. Lifecycle events carry correlation fields such as job ID, delivery ID, branch, commit, stage, and version.

Event names
webhook_server_startwebhook_acceptedwebhook_branch_mismatchwebhook_duplicatewebhook_queue_failurewebhook_rebuild_failedwebhook_rejectedwebhook_job_startedwebhook_job_succeededwebhook_job_failedwebhook_job_rolled_backwebhook_job_recovered

webhook_rejected covers authenticated but unreadable requests. Signature rejections are logged locally only, so unauthenticated traffic cannot trigger backend traffic. Local logs include app, job ID, delivery ID, branch, and commit even when remote monitoring is disabled.