Recipes: ops, IT & on-chain
Two pipelines for the things you want to know about the moment they happen — your site going down or a cert expiring, and money moving on-chain.
Uptime + SSL watch
Check that your site responds every hour, and that its TLS certificate isn’t about to expire — alerting
you the moment either breaks. Two small jobs, each using an onFailure handler:
the check just fails, and the handler does the paging.
The shape:
uptime (http, hourly) --fails--> onFailure: uptime-alert (telegram)
ssl-check (terminal, weekly) --fails--> onFailure: ssl-alert (telegram)A non-200 fails the http run, which triggers its onFailure handler:
# jobs/ops/uptime.yaml
id: uptime
name: Uptime check
group: ops
type: http
method: GET
url: '{{ $vars.siteUrl }}'
expectStatus: 200
retry:
maxAttempts: 3
backoff: exponential
delayMs: 5000
onFailure: uptime-alert
schedule:
- { kind: hourly, minute: 0 }# jobs/ops/uptime-alert.yaml — the handler fires with $parent.error when a check fails
id: uptime-alert
name: Uptime alert
group: ops
type: telegram
credential: telegram_bot
chatId: '{{ $vars.opsChatId }}'
message: |
:red_circle: {{ $parent.name }} is down: {{ $parent.error }}openssl x509 -checkend exits non-zero when the cert expires within the given window (1814400s = 21
days), so the terminal job fails → the handler alerts:
# jobs/ops/ssl-check.yaml
id: ssl-check
name: SSL expiry check
group: ops
type: terminal
cwd: /tmp
command: >
echo | openssl s_client -connect {{ $vars.siteHost }}:443 -servername {{ $vars.siteHost }} 2>/dev/null
| openssl x509 -checkend 1814400 -noout
onFailure: ssl-alert
schedule:
- { kind: weekly, weekday: mon, time: '06:00' }# jobs/ops/ssl-alert.yaml
id: ssl-alert
name: SSL alert
group: ops
type: telegram
credential: telegram_bot
chatId: '{{ $vars.opsChatId }}'
message: ':warning: TLS cert for {{ $vars.siteHost }} expires within 21 days — renew it.'Tighter interval:
hourlyis the finest single clock point. For every-15-minutes checks, add morehourlypoints (minute: 0,15,30,45) to the same job.
Treasury wallet watch
Watch an ERC-20 (e.g. USDC) for transfers into your treasury wallet, alert on the big ones in real time, and log every hit — read-only, no keys, no signing.
The shape:
usdc-in (evm-watch) → usdc-large (filter) → usdc-alert (telegram) + usdc-log (sheets)An evm-watch trigger fires per matching on-chain event (its credential holds the full
RPC URL so a keyed endpoint stays out of the YAML):
# jobs/treasury/usdc-in.yaml
id: usdc-in
name: USDC into treasury
group: treasury
type: evm-watch
credential: eth-rpc
chainId: 8453 # Base — pinned as a wrong-chain guard
contractAddress: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913' # USDC on Base
eventSignature: 'Transfer(address indexed from, address indexed to, uint256 value)'
indexedFilters:
to: '{{ $vars.treasuryWallet }}'
confirmations: 5
pollSeconds: 30
schedule: []The value is USDC base units (6 decimals), so 10000000000 = $10,000:
# jobs/treasury/large-only.yaml
id: usdc-large
name: Large transfers only
group: treasury
type: filter
input: '{{ $parent.json.args.value }}'
operator: gt
value: '{{ $vars.minUsdcBaseUnits }}'
schedule:
- { kind: afterParent, parent: usdc-in, on: succeeded }# jobs/treasury/alert.yaml
id: usdc-alert
name: Big transfer alert
group: treasury
type: telegram
credential: telegram_bot
chatId: '{{ $vars.treasuryChatId }}'
message: |
:moneybag: USDC in from {{ $parent.json.args.from }} — {{ $parent.json.args.value }} (base units)
tx {{ $parent.json.transactionHash }}
schedule:
- { kind: afterParent, parent: usdc-large, on: succeeded, branch: pass }# jobs/treasury/log.yaml
id: usdc-log
name: Log transfer
group: treasury
type: sheets
credential: google_sa
spreadsheetId: '{{ $vars.treasurySheetId }}'
operation: appendRow
range: Transfers!A:D
values: |
{{ $now.iso }}
{{ $parent.json.args.from }}
{{ $parent.json.args.value }}
{{ $parent.json.transactionHash }}
schedule:
- { kind: afterParent, parent: usdc-large, on: succeeded, branch: pass }Read-only by design:
evm-watchonly ever callseth_getLogsand friends — it never holds a private key or sends a transaction. Alerting on money movement is safe; moving money is not something Cronable does.