# Addons

The Pingtower daemon deliberately runs no probes — it alerts on what your
apps tell it. The **addons agent** is the other half: a single Go binary you
install on any box *outside* the infrastructure you're watching. It runs
small checks — an HTTP request, a TCP connect, a DNS lookup, a certificate's
expiry, a ping — on a schedule and reports each measurement as a structured
event through the same [ingest plane](/api/) everything else uses. No new
server, no special plumbing: probes are just events, and your tower rules
threshold them.

It works identically against the hosted service and a self-hosted box.

## The addons

| Addon | Measures |
| --- | --- |
| `httpcheck` | `dns_ms`, `connect_ms`, `tls_ms`, `ttfb_ms`, `total_ms`, `body_bytes`, status and body expectations |
| `tcpcheck` | `dns_ms`, `connect_ms`, `total_ms` |
| `dnscheck` | `resolve_ms`, `answers`, expected-record checks |
| `tlscert` | `days_until_expiry`, `handshake_ms`, `chain_length` |
| `icmpping` | `rtt_min/avg/max_ms`, `loss_pct` |
| `sshcheck` † | connect/handshake/auth/command timings, `exit_code` |
| `portscan` † | `open_ports`, `unexpected_ports`, `missing_ports` |
| `traceroute` † | `hops`, `path_change`, per-hop RTTs |
| `logwatch` † | pattern `matches`, `last_write_age_s`, rotation handling |

† Privileged and **off by default** — each needs a credential, an external
binary, a heavier network footprint, or read access to a host file, and a
config naming one fails at startup unless the agent's own
`agent.enable_addons` list turned it on.

A healthy check reports at level 200; a failing one reports **down** at
level 400 (configurable to 500 for checks that should page). Repeats
collapse by message shape like every other event, so a flapping check is one
alert with a count, not a page storm.

## Download


Version `0.1.1`, signed with the addons release key
([`addons-release.pub`](/dist/addons/addons-release.pub) — a different key
than the daemon's, so either can rotate without invalidating the other):

| Artifact | Size | SHA-256 |
| --- | --- | --- |
| [addons_0.1.1_linux_amd64.tar.gz](/dist/addons/addons_0.1.1_linux_amd64.tar.gz) | 3.5 MB | `032c28bb958448cee00d5fb50751fc7a5a75d6aa7efcaebaf0b5f6586b31d0ff` |
| [addons_0.1.1_linux_arm64.tar.gz](/dist/addons/addons_0.1.1_linux_arm64.tar.gz) | 3.2 MB | `30a6f8f45ee6820f950fe13ad15f68c6bcf3b914954400437d23ed8e0bd2c91c` |
| [SHA256SUMS](/dist/addons/SHA256SUMS) + [.minisig](/dist/addons/SHA256SUMS.minisig) | | |

Verify before unpacking — the first command checks the sums file was signed
by this project, the second checks your tarball matches it. Skipping either
lets a tampered tarball and a tampered checksum file agree with each other:

```sh
minisign -Vm SHA256SUMS -p addons-release.pub
sha256sum --ignore-missing -c SHA256SUMS
```


## Install

```sh
tar xzf addons_<version>_linux_<arch>.tar.gz
cd addons_<version>_linux_<arch>
sudo ./install.sh
```

The installer creates a dedicated `addons` system user, installs the binary
to `/usr/local/bin/addons`, and sets up `/etc/addons` and a systemd unit.
Each tarball's contents are covered by a signed manifest that `install.sh`
verifies before it runs anything as root.

## Try one check first

`addons check` runs a single check once and prints the event it would send.
Add `--report` to send it to your tower — the ingest token comes from the
environment, never a flag, so it stays out of shell history:

```sh
export PINGTOWER_INGEST_TOKEN=pti_<tenant>_…
export PINGTOWER_URL=https://api.pingtower.com   # or your own box
addons check httpcheck --param url=https://example.com --report
# UP in 101ms — sent, template f5b97ca2801d, alerted
```

Measurements travel as JSON numbers, so a tower rule thresholds them
directly, with no agent-side alert configuration at all:

```sh
# Page when time-to-first-byte crosses 500ms:
curl -s "$PT/v1/projects/probes/rules" -H "Authorization: Bearer ptk_…" \
  -d '{"name":"slow-ttfb","match":{"tags":["addon:httpcheck"]},
       "conditions":[{"key":"ttfb_ms","op":"gte","value":500}]}'

# Or alert on any check going down:
curl -s "$PT/v1/projects/probes/rules" -H "Authorization: Bearer ptk_…" \
  -d '{"name":"probe-down","match":{"min_level":400}}'
```

The same idea covers certificate expiry (`days_until_expiry lte 14`), DNS
drift, packet loss — every number in the table above is a rule away from
paging someone.

## Run it as an agent

`addons agent` runs a whole config on a schedule until you stop it, spooling
to disk through tower outages and delivering when the tower returns:

```yaml
# /etc/addons/agent.yaml
agent:
  id: probe-fra-01
  heartbeat: 5m
  tower:
    url: https://api.pingtower.com
    token_env: PINGTOWER_INGEST_TOKEN

defaults:
  interval: 60s
  timeout: 10s
  report: on_change

checks:
  - id: site
    addon: httpcheck
    params: { url: "https://example.com", expect_status: 200 }
  - id: cert
    addon: tlscert
    params: { address: "example.com:443" }
```

`report: on_change` keeps quiet checks quiet; the heartbeat reports on the
agent itself, so its own health is a metric too.

