Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.pylon.to/llms.txt

Use this file to discover all available pages before exploring further.

A trigger tells Pylon what event should activate a pipeline. Every pylon has exactly one trigger, defined under the trigger key in pylon.yaml. Pylon supports two trigger types: webhook for HTTP-driven events and cron for scheduled jobs.

Webhook triggers

A webhook trigger listens for an HTTP POST to a specific path on Pylon’s built-in server. When a request arrives at that path, Pylon loads the matching pylon and starts a job. Pylon listens on port 8080 by default. You can change this in ~/.pylon/config.yaml under server.port.
trigger:
  type: webhook
  path: /sentry-triage

Webhook fields

FieldRequiredDescription
typeYesMust be webhook.
pathYesThe URL path Pylon listens on, e.g. /sentry-triage. Must start with /.
secretNoA shared secret used to validate the request signature. Supports ${ENV_VAR} references.
signature_headerNoThe HTTP header containing the HMAC signature, e.g. Sentry-Hook-Signature or X-Hub-Signature-256. Required when secret is set.
public_urlNoOverrides the global server.public_url for this pylon’s displayed webhook URL.

Signature verification

When you set both secret and signature_header, Pylon computes an HMAC signature for each incoming request, compares it to the header value, and rejects the request with 401 Unauthorized on mismatch.
trigger:
  type: webhook
  path: /sentry-triage
  secret: "${SENTRY_CLIENT_SECRET}"
  signature_header: Sentry-Hook-Signature
Store secrets in ~/.pylon/.env or ~/.pylon/pylons/<name>/.env and reference them with ${VAR_NAME}. See secrets files for the resolution rules.

Algorithm

  1. Read the raw request body (bytes, unparsed).
  2. Compute HMAC-SHA256(secret, body).
  3. Hex-encode the digest as a lowercase string.
  4. Compare that string to the value of the header named by signature_header, using a constant-time comparison.
Pylon expects the signature header value to be a bare lowercase hex digest with no prefix or scheme. If the sender wraps the digest (for example, GitHub’s sha256=<hex> format), Pylon does not strip the prefix and the comparison will fail.
If you leave signature_header empty, Pylon skips verification entirely even when secret is set. Always set both fields together.

Provider compatibility

SenderHeaderFormatWorks out of the box?
SentrySentry-Hook-Signaturebare hexYes
Slack (request signing v0)X-Slack-Signaturev0=<hex>No — Slack prefixes the digest and also requires a timestamped payload
GitHubX-Hub-Signature-256sha256=<hex>No — GitHub prefixes the digest
Custom senderanybare hexYes, when you control the sender
For providers whose format does not match (GitHub, Slack), put an intermediary (nginx, Caddy, a tiny Go/Node shim) in front of Pylon to validate the native signature first and forward the request without the verification headers. Or skip signature verification at the Pylon layer and rely on network-level isolation.

Generating a signature by hand

Useful when scripting a custom sender or re-testing with curl:
SECRET="whsec_example"
BODY='{"event":"ping"}'

SIG=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print $2}')

curl -X POST https://pylon.example.com/sentry-triage \
  -H "Content-Type: application/json" \
  -H "Sentry-Hook-Signature: $SIG" \
  -d "$BODY"
Field-level reference for trigger.secret and trigger.signature_header lives in pylon.yaml.

Exposing the webhook publicly

Pylon’s server runs locally. To receive webhooks from external services (GitHub, Sentry, etc.), you need to expose it to the internet.
Point your reverse proxy (nginx, Caddy, Traefik) at Pylon’s local port and set public_url in your global config or per-pylon:
# ~/.pylon/config.yaml
server:
  port: 8080
  public_url: https://pylon.example.com

Cron triggers

A cron trigger runs a pylon on a schedule, using standard cron expression syntax.
trigger:
  type: cron
  cron: "0 9 * * 1-5"

Cron fields

FieldRequiredDescription
typeYesMust be cron.
cronYesA standard 5-field cron expression.

Cron expression format

┌───── minute (0-59)
│ ┌───── hour (0-23)
│ │ ┌───── day of month (1-31)
│ │ │ ┌───── month (1-12)
│ │ │ │ ┌───── day of week (0-6, Sunday=0)
│ │ │ │ │
* * * * *
Common examples:
0 9 * * 1-5    # Weekdays at 9am
0 9 * * 1      # Every Monday at 9am
0 */6 * * *    # Every 6 hours
Cron pylons run the agent directly and post results to the configured channel. There is no approval flow for cron triggers — approval: true has no effect when type: cron.

Cron example

trigger:
  type: cron
  cron: "0 9 * * 1"  # Every Monday at 9am

workspace:
  type: git-clone
  repo: git@github.com:acme/nexus.git
  ref: main

agent:
  prompt: |
    Audit this codebase for security vulnerabilities, outdated
    dependencies, and code quality issues. Provide a summary report.
  timeout: 30m

channel:
  message: "Weekly codebase audit"

Coming soon

The following trigger types are planned but not yet available:
  • Chat command — trigger a pylon from a Telegram or Slack message
  • API call — trigger a pylon via a direct API request with auth