Decisions, lessons, blockers. Append-only. Update as we go.
Decided
git3.techno-world.net/lrosales/broad-announce (private).companies.fcm_shared).info | warning | critical | inminent_colapse.
Only inminent_colapse bypasses quiet hours.(source_id, dedupe_key), attach
dedupe_count so user sees "×N in 60s" not N pushes.Open (was) → Resolved 2026-06-13
individuals row + generates one-time invite code;
user runs /start <invite_code> in the Telegram bot; bot
matches the code, links telegram_chat_id to the
pre-existing individual, and burns the code. Stricter path:
prevents drive-by bot self-registration, keeps
individuals auditable.title/body strings (or use the locale on the
subscription to key into their own lookup table before
calling our API). Alert schema carries locale and
title/body already-resolved. We can add a translation
layer in v2 if customers ask.Lessons (already)
alerts.<company_id> is fine; alerts.<company_id>.<source_id>
would let us scale router consumer groups per source. Locked
in §6 of ARCHITECTURE.md.2026-06-13 — added gRPC ingest (option a)
buf generate build step.StreamAlerts(Alert) → Ack carries the same dedupe_count
contract as HTTP/WS/MQTT.proto/broadannounce/v1/ingest.proto; server
stub in internal/grpcserver/, reusable client in
internal/grpcclient/. Auth = API key in metadata + optional
mTLS. Per-stream rate limit + 256 in-flight cap = natural
backpressure.2026-06-13 — M10 split: option C
loadgen/ tool is the new home for that.2026-06-13 — three open questions resolved
testfakes/). M10 must not burn 50k FCM credits.deliverd binary in the bench profile, not a NATS
subject drop. More realistic: we exercise the real broker +
router + the message shape deliverd would consume.loadgen ships as a single Docker image with all
four binaries; entrypoint picks one via the image's
command: field in the compose / k8s spec.2026-06-13 — Source protection (throttling) is in
rate_limit_per_sec, max_payload_bytes,
max_concurrent_connections, quarantine_*).2026-06-13 — M0 shipped (12 commits, 2888 LoC)
What landed:
cmd/{ingestd,routerd,deliverd,admind}/ — four Go service mainscmd/ingestd/http.go — HTTP POST handler implementing
SPEC §22 layers 1, 3, 4, 5 + Stripe-style HMAC authinternal/alert — Alert v1 type + Validate() (183 LoC + 103
LoC tests)internal/broker — NATS JetStream wrapper, three streams
(ALERTS/DELIVERIES/DLQ) auto-createdinternal/dedupe — 60s SET NX EX + INCR (Redis-required tests
pass against a host-local Redis)internal/ratelimit — per-second INCR bucket (Redis-required
tests pass)internal/observability — slog + Prometheus registry, the
IngestdMetrics struct matches SPEC §22 metric namesinternal/httpserver — shared /health + /metrics scaffoldinternal/config — env-driven Common + Ingestdloadgen/cmd/http/ — loadgen-http with --mode normal,
HMAC signing, 70/25/4/1 severity mix, dedupe-pct knobloadgen/go.mod — separate module per SPEC §21, replace
directive points at the parent moduledocker-compose.yml + Dockerfile — single-host stack, all
5 binaries in one imageM0_VERIFICATION.md — 8-step smoke testdeploy/prometheus/prometheus.yml — scrapes all 5 servicesWhat's NOT in M0 (and not supposed to be):
Module path: git3.techno-world.net/lrosales/broad-announce.
Loadgen module path: git3.techno-world.net/lrosales/broad-announce/loadgen.
All pushed: 7cd922c..49b2dba on master.
2026-06-13 — Port convention
Project rule: app HTTP services use 8800–8899 (ingestd 8800,
routerd 8801, deliverd 8802, admind 8803, loadgen metrics 8891,
fakefcmd 8820). Canonical ports stay (5432, 4222, 6379, 1883,
9090, 3000). Reason: 8080+ collides with workstation tooling.
SPEC §18 now has a port-conventions sub-section. Commit: d76aa0b.
2026-06-13 — M1 code-complete (8 commits, awaiting live smoke)
What landed:
migrations/001_init.up.sql — companies, individuals,
fcm_tokens (the M1 minimum schema; full SPEC §4 lands in M2
as additional migrations)migrations/002_deliveries.up.sql — deliveries table
(status: pending|sent|failed|dlq; payload jsonb for M8 replay)migrations/seed.sql — idempotent; 1 company, 1 individual,
1 tokeninternal/postgres — pgxpool wrapper with retry-on-startupcmd/seed — applies *.up.sql in lexical order, then seed.sqltestfakes/fakefcmd — 70 lines, /health + /v1/.../messages:send,
--fail-rate knobinternal/routing — Resolver with ResolveTokens (M1 broadcast
via single SQL join; M2 swaps for the rules engine)cmd/routerd — M1 main: subscribes to alerts.>, resolves
recipients, publishes one deliveries.fcm. per token
cmd/deliverd — M1 main: subscribes to deliveries.fcm.>,
builds FCM HTTP v1 message body (M3 swap is a no-op at this
layer), posts to BA_FAKECMD_URL, writes a deliveries rowWhat we agreed to defer (per the user):
What's NOT in M1 (and not supposed to be):
Pushed: 264d284..9cf68a1 on master (5 commits for M1 code,
plus the d76aa0b port shift).
The user still has to actually run docker compose up and the
M1 verification steps before M1 is fully done.