This is the live smoke test for the M2 milestone (Recipient resolution, SPEC §6). It exercises the rules engine that replaces M1's broadcast with:
source.allowed_targets (groups + individuals)routing_rules with match_expr overridessubscriptions with min_severity, channel_mask, and
quiet_hoursinminent_colapse bypasses quiet hoursHard-fail (your Q2 answer): if the resolver returns zero targets, the alert is dropped and logged. No silent broadcast.
docker compose up -ddocker compose up seed (apply M2
migration + both seeds)acme-001:prom-prod source is s3cret-acme
(matches BA_INGESTD_SOURCES env)| individual | subscription (severity, quiet hours) | notes |
|---|---|---|
| ind-acme-001 (Alice) | NULL, no quiet hours | gets every alert from prom-prod |
| ind-acme-002 (Bob) | critical, no quiet hours |
only critical and above |
| ind-acme-003 (Carol) | NULL, 00:00–23:59 UTC | everything except inminent_colapse |
Groups: sre = Alice + Bob.
Source acme-001:prom-prod allowed_targets:
group:sreindividual:ind-acme-003Routing rule: category=storage + data.host=db-prod-03 → individual:ind-acme-002 (Bob), priority 10.
docker compose exec postgres psql -U ba -d ba -c "
SELECT i.id, i.full_name,
s.min_severity, s.quiet_hours_start, s.quiet_hours_end, s.tz
FROM individuals i
LEFT JOIN subscriptions s
ON s.individual_id = i.id AND s.source_id = 'prom-prod'
ORDER BY i.id;
"
Expected: 3 rows. Carol's quiet_hours_start=00:00:00 and quiet_hours_end=23:59:00.
warning storage alertSECRET=s3cret-acme
BODY='{"company_id":"acme-001","source_id":"prom-prod","severity":"warning","category":"storage","title":"Disk 80% full","body":"db-prod-04","data":{"host":"db-prod-04"},"dedupe_key":"m2-s2"}'
TS=$(date +%s)
SIG=$(printf '%s.%s' "$TS" "$BODY" | openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print $2}')
curl -s -X POST http://localhost:8800/v1/ingest \
-H "Content-Type: application/json" \
-H "X-BA-Signature: t=$TS,v1=$SIG" \
--data "$BODY"
Wait 2s, then check deliveries:
docker compose exec postgres psql -U ba -d ba -c "
SELECT alert_id, individual_id, channel, status FROM deliveries
WHERE alert_id IN (SELECT id FROM deliveries WHERE last_error IS NULL ORDER BY id DESC LIMIT 1)
ORDER BY individual_id;
"
Expected recipients: Alice only.
Why:
critical storage alert for db-prod-04SECRET=s3cret-acme
BODY='{"company_id":"acme-001","source_id":"prom-prod","severity":"critical","category":"storage","title":"Disk full","body":"db-prod-04","data":{"host":"db-prod-04"},"dedupe_key":"m2-s3"}'
TS=$(date +%s)
SIG=$(printf '%s.%s' "$TS" "$BODY" | openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print $2}')
curl -s -X POST http://localhost:8800/v1/ingest \
-H "Content-Type: application/json" \
-H "X-BA-Signature: t=$TS,v1=$SIG" \
--data "$BODY"
Wait 2s, check deliveries.
Expected recipients: Alice + Bob.
Why:
critical storage alert for db-prod-03 (rule match)SECRET=s3cret-acme
BODY='{"company_id":"acme-001","source_id":"prom-prod","severity":"critical","category":"storage","title":"Disk full","body":"db-prod-03","data":{"host":"db-prod-03"},"dedupe_key":"m2-s4"}'
TS=$(date +%s)
SIG=$(printf '%s.%s' "$TS" "$BODY" | openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print $2}')
curl -s -X POST http://localhost:8800/v1/ingest \
-H "Content-Type: application/json" \
-H "X-BA-Signature: t=$TS,v1=$SIG" \
--data "$BODY"
Expected recipients: Alice + Bob (rule doesn't add anyone new — Bob is already in via group+severity).
This step proves the rule doesn't double-route. The SQL UNION just dedupes the candidate set.
warning alert that ONLY matches the rule (and would normally skip Bob)This is the rule-fires scenario. Add a new individual that has min_severity=warning (Alice does). But the rule points at Bob specifically, and Bob has min_severity=critical. So a warning alert matching the rule goes to Bob? No — per the algorithm, subscriptions still apply on top of rules. So a warning storage alert for db-prod-03 should go to Alice only (Bob's subscription filters it out).
SECRET=s3cret-acme
BODY='{"company_id":"acme-001","source_id":"prom-prod","severity":"warning","category":"storage","title":"Disk 80% on prod-03","body":"pre-empt","data":{"host":"db-prod-03"},"dedupe_key":"m2-s5"}'
TS=$(date +%s)
SIG=$(printf '%s.%s' "$TS" "$BODY" | openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print $2}')
curl -s -X POST http://localhost:8800/v1/ingest \
-H "Content-Type: application/json" \
-H "X-BA-Signature: t=$TS,v1=$SIG" \
--data "$BODY"
Expected recipients: Alice only. Rule matched Bob, but his min_severity filter (critical) drops warning.
inminent_colapse alert for any hostThis is the quiet-hours bypass test. Carol is in quiet hours; an inminent_colapse should reach her anyway.
SECRET=s3cret-acme
BODY='{"company_id":"acme-001","source_id":"prom-prod","severity":"inminent_colapse","category":"power","title":"PDU overload imminent","body":"rack-B","data":{"host":"rack-b"},"dedupe_key":"m2-s6"}'
TS=$(date +%s)
SIG=$(printf '%s.%s' "$TS" "$BODY" | openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print $2}')
curl -s -X POST http://localhost:8800/v1/ingest \
-H "Content-Type: application/json" \
-H "X-BA-Signature: t=$TS,v1=$SIG" \
--data "$BODY"
Expected recipients: Alice + Carol (severity bypasses Carol's quiet hours; Bob's min_severity=critical, inminent_colapse=rank 3 ≥ critical=rank 2 → in).
Wait — actually Bob's min_severity=critical, and inminent_colapse rank 3 is > critical rank 2, so Bob IS in. Let me re-check:
| individual | in? | why |
|---|---|---|
| Alice | in | no min |
| Bob | in | min=critical, inminent_colapse > critical |
| Carol | in | quiet hours bypassed for inminent_colapse |
Expected: Alice + Bob + Carol. All three.
Send a warning alert via a source that has a valid HMAC secret (so
ingestd doesn't 401) but no subscriptions and no routing-rule
matches. The seed registers acme-001:prom-prod only, so the easiest
is to add a second source to BA_INGESTD_SOURCES in compose, but
that requires a restart. The cheaper path: temporarily revoke all
subscriptions in psql, then send an alert, then restore.
# revoke
docker compose exec postgres psql -U ba -d ba -c "
UPDATE subscriptions SET status = 'paused' WHERE company_id = 'acme-001';
"
# wait a moment so the next request re-resolves
sleep 1
# send
SECRET=s3cret-acme
BODY='{"company_id":"acme-001","source_id":"prom-prod","severity":"info","category":"storage","title":"no recipients","data":{"host":"db-prod-04"},"dedupe_key":"m2-s7"}'
TS=$(date +%s)
SIG=$(printf '%s.%s' "$TS" "$BODY" | openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print $2}')
curl -s -X POST http://localhost:8800/v1/ingest \
-H "Content-Type: application/json" \
-H "X-BA-Signature: t=$TS,v1=$SIG" \
--data "$BODY"
Expected: 202 from ingestd, then routerd logs
"zero recipients, dropping". No delivery row for this alert.
# verify
docker compose logs --no-color --since 1m routerd 2>&1 | grep -E "zero recipients" | tail -3
# restore
docker compose exec postgres psql -U ba -d ba -c "
UPDATE subscriptions SET status = 'active' WHERE company_id = 'acme-001';
"
After all steps, run:
docker compose exec postgres psql -U ba -d ba -c "
SELECT alert_id, individual_id, status
FROM deliveries
WHERE alert_id LIKE '0000%'
ORDER BY id DESC
LIMIT 15;
"
Each step's expected recipient set is in the per-step notes above. The total across steps 2–6 should be 1+2+2+1+3 = 9 deliveries (assuming 0 dedupes within the 60s window).