feat(relay): add early startup lifecycle logs - #7258
Conversation
🔐 Codex Security Review
|
1f7db9b to
3f5b95b
Compare
Record bounded, secret-safe lifecycle events for the relay's earliest startup phases. Keep the contract log-only so pre-exporter failures retain their real event time. Co-authored-by: Ravneet Arora <rarora@squareup.com> Signed-off-by: Ravneet Arora <rarora@squareup.com>
3f5b95b to
8faf752
Compare
|
P1 — post-bind metrics exporter failure remains detached and silent At exact head This also leaves the stated contract of #7238 incomplete: that issue explicitly calls for retaining supervision of the exporter lifecycle, while this PR says Please either:
The existing successful-start and occupied-port tests cover pre-bind behavior, but do not exercise a post-bind exporter exit. |
|
🤖 Thanks — confirmed. Dropping Tokio's I took option 2 to keep this PR within its logs-only early-startup scope:
No production code or staged image changed as part of this scope correction. |
wpfleger96
left a comment
There was a problem hiding this comment.
One blocking correctness gap remains. The bounded schema, secret-safe reasons, early stderr sink, exact terminal accounting, and child-process coverage are otherwise solid.
Quality scores:
- Minimalism: 9/10 — the schema and typed classifications are tightly bounded.
- Elegance: 9/10 — phase ownership and aggregate accounting are clear and fit the startup path.
- Correctness: 8/10 — bind/build/conflict handling is covered, but the exporter lifecycle required by #7238 is still detached.
CI is green at exact head 8faf7526822a119efa035e58b2b3c59aa67fc81d.
| .map_err(|_error| MetricsInstallError::RecorderConflict)?; | ||
| describe_readiness_metrics(); | ||
| describe_db_pool_metrics(); | ||
| tokio::spawn(exporter); |
There was a problem hiding this comment.
IMPORTANT — Correctness: This still drops the exporter JoinHandle immediately after reporting metrics_bind/succeeded. If this future later returns Err or panics, the relay keeps running with a dead metrics endpoint and no in-process failure record. That leaves #7238’s explicit exporter-supervision requirement incomplete. Please retain and supervise the task, surface an unexpected exit with a bounded secret-safe reason, and add a production-seam regression that forces a post-bind exit. If supervision is intentionally deferred, this PR should stop closing #7238 and link a follow-up issue instead.
There was a problem hiding this comment.
Independent live verification at the same exact head confirms this failure mode through the production API seam:
- Built
buzz-relayat8faf7526822a119efa035e58b2b3c59aa67fc81d(release artifact SHA-256d98e1dead1086cd126437c195e2d5b971f742e6d53523679af80ea7a1a9b7c40). - Started
BootTracker, ran requiredMetricsBind, calledmetrics::try_install(), and successfully scraped the actual bound HTTP exporter. - Dropped the exporter’s Tokio runtime to force a post-bind task exit while leaving the process alive. The endpoint then refused connections, but the process remained alive.
- Lifecycle evidence remained limited to
metrics_bindand aggregateprocess_telemetryterminal records withstatus: "succeeded"; no terminal or degradation record reported the exporter’s disappearance.
Observed probe result:
PROBE bound_and_scrapeable=true port=65429
PROBE child_alive_after_exporter_exit=true connect_after_runtime_drop=Err(... ConnectionRefused ...)
This independently confirms that the detached JoinHandle leaves stale, materially false process-local lifecycle evidence after metrics loss. External scrape alerts can detect absence, but they do not supervise, restart, terminate, or correct lifecycle state. The IMPORTANT correctness severity and required fix above stand. The probe worktree, listener, and process were cleaned up afterward.
There was a problem hiding this comment.
🤖 Thanks for the independent live reproduction. I agree the post-bind exporter terminal is currently unobserved.
This PR is taking the deferral option from the original comment. The current PR body no longer closes #7238; it explicitly scopes #7258 to the logs-only early-startup slice and links #7284. #7284 tracks retained exporter ownership, bounded secret-safe runtime-failure reporting, owned drain/teardown under an independent deadline, and the post-bind production-seam regressions.
The existing metrics_bind and process_telemetry terminals remain immutable startup receipts; #7284 must report the later runtime failure without emitting a second startup terminal. No production code or staged image changed for this scope correction.
Please re-review the corrected scope when you can.
There was a problem hiding this comment.
I re-reviewed the corrected scope. Removing the closing reference to #7238 and explicitly tracking post-bind exporter supervision in #7284 satisfies the deferral option from my original finding. The detached exporter remains a real pre-existing runtime gap, but it is no longer an unfulfilled claim of this logs-only PR, so this blocker is resolved.
wpfleger96
left a comment
There was a problem hiding this comment.
The scope correction resolves my requested change: this PR now explicitly delivers only the logs-only early-startup slice, does not close #7238, and tracks post-bind exporter supervision in #7284. The bounded schema, secret-safe reasons, early stderr sink, exact startup terminal accounting, and child-process coverage meet that narrower contract.
Quality scores:
- Minimalism: 9/10 — the fixed schema and typed classifications stay tightly bounded.
- Elegance: 9/10 — phase ownership and aggregate accounting are clear and fit the startup path.
- Correctness: 9/10 — the implementation and tests cover the stated logs-only startup contract; the independently reproduced post-bind runtime gap is explicitly deferred to #7284 rather than claimed here.
All applicable CI checks are green at exact head 8faf7526822a119efa035e58b2b3c59aa67fc81d.
…rcement * origin/main: feat(relay): add early startup lifecycle logs (#7258) docs(nip-fi): add Blossom kind-24242 media possession-proof exception (#7278) fix(desktop): wrap message tables within the available pane (#7279) Signed-off-by: Hayt <9e1c23a3fd83f61da34420e4e88ff1b16e45cafcc0cd9019eb07d4ecfa8ca9b0@buzz.block.builderlab.xyz>
…ssion-deny * origin/main: feat(relay): add early startup lifecycle logs (#7258) Signed-off-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
…-enforcement * origin/main: feat(relay): add early startup lifecycle logs (#7258) Signed-off-by: Hayt <9e1c23a3fd83f61da34420e4e88ff1b16e45cafcc0cd9019eb07d4ecfa8ca9b0@buzz.block.builderlab.xyz>
* origin/main: chore(release): release Buzz Desktop version 0.5.21 (#7301) fix(scripts): copy global-agent-config.json in buzz-adopt-prod-agents (#7303) feat(relay): add early startup lifecycle logs (#7258) docs(nip-fi): add Blossom kind-24242 media possession-proof exception (#7278) fix(desktop): wrap message tables within the available pane (#7279) Signed-off-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
## Why
Current pool metrics show checkout outcomes and pool state after the
fact. They do not show when a checkout began or which writer-connection
setup step failed. During startup and pool growth, operators need to
distinguish pool saturation from a slow or unsafe connection setup.
## What
- Count every instrumented database checkout when it starts, with a
fixed operation label.
- Measure writer-pool creation and every physical writer connection
across `physical_connect`, `created_at_floor`, `session_timeouts`,
`isolation`, and `ready`.
- Publish fixed-cardinality start, terminal-outcome, duration, and
waiter metrics without database URLs, SQL, raw errors, connection
ordinals, or per-connection lifecycle logs.
- Document how to identify the active setup bottleneck during a rollout
or incident.
## How
The existing typed checkout wrapper records starts and current waiters.
The huddle-history path uses that wrapper instead of a raw pool
checkout.
The production SQLx `after_connect` hook records start, terminal
outcome, and duration for each sequential safety step. A drop guard
records cancellation once if setup exits before a terminal result. Fixed
enums keep every label bounded.
For a phase, `started_total - sum(attempts_total)` is the number of
in-progress attempts on that pod. Because setup is sequential, a later
phase starting also proves the earlier phases succeeded.
The database path emits metrics only. This PR intentionally does not add
per-connection lifecycle receipts or direct `stderr` writes.
## Risk
Medium. This changes the production writer-pool `after_connect` hook and
adds one counter update to instrumented checkout paths. It does not
change the database safety statements or their failure behavior. The
telemetry has fixed labels and no per-connection identifiers.
## Testing
At exact head `ffb5fcb114a9c986ffd0cb2cfc9d413a32d5b1aa`, a release
relay was started against isolated local PostgreSQL, Redis, and MinIO
services. The main health endpoint returned `ok`, readiness returned
`{"status":"ready"}`, the connection-step metrics were exported, and no
database lifecycle receipts were emitted. A release CLI then created a
channel, sent a message, and read the same event back successfully.
The earlier staging deployment used pre-rebase head
`f58e9480a4f068db0c591f604fd6800fdd4bfc45`. The deployed
multi-architecture image came from [GitHub Actions run
33780888255](https://github.com/block/buzz/actions/runs/33780888255),
manifest
`sha256:b9351fa644e08376cbe1999f9bee311d33d1799a68eadef7929c4f862a832fec`.
The [staging
deployment](squareup/builderbot-platform-core-infrastructure#317)
brought both pods in ReplicaSet `buzz-6c8758bd7d` to Ready with zero
restarts, and the connection metrics produced data in the [rollout
dashboard](https://app.datadoghq.com/dashboard/tm7-qxr-wt2/buzz-startup--rollout-safety).
That staging image predates the cleanup that removed per-connection
lifecycle logs; the metric schema and database safety statements are
unchanged.
## Verification
- `cargo test -p buzz-db -- --test-threads=1`: 128 tests passed across
the package and integration target; 255 opt-in tests remained ignored.
- Four opt-in production-path PostgreSQL regressions passed: initial
minimum connections, post-startup pool growth, isolation failure, and
session-timeout setup failure.
- All 35 relay media tests passed against the isolated PostgreSQL
database.
- Rust formatting, workspace clippy, desktop/Tauri clippy, web checks,
mobile analysis, security checks, and file-size policy checks passed.
- The repository-wide unit stage also exposed three unrelated existing
`buzz-acp` failures: one timing-sensitive keepalive test and two
environment-default tests.
## Bigger picture
This is the database-metrics part of the startup and rollout
observability work. The early-startup lifecycle foundation merged in
#7258, so the rebase removed that duplicate commit from this PR.
Originating discussion:
buzz://message?channel=6ac85131-70cd-4bda-a031-38d34114934e&id=fa2bed181c092697210a60bb6eedc55a665d5c1c6cabc1413a04686647011f71
Generated with Codex
---------
Signed-off-by: Ravneet Arora <rarora@squareup.com>
* fix(ci): don't run desktop tests for purely mobile client changes (block#7709) Mobile-only PRs currently trigger desktop CI because the desktop test filter's glob inadvertently matches every path outside Tauri. Fix that filter so that pure mobile changes skip desktop builds, tests, and their relay artifact producer. ### Verification - 12 regression tests exercise the exact pinned paths-filter action on mobile, desktop, Tauri, backend, mixed, workflow, and documentation changes. - Restoring the bad glob makes the mobile-only regression test fail. - The filter tests, existing required-context isolation contract, workflow syntax validation, and Biome pass locally. --------- Signed-off-by: Tom Brow <tomb@block.xyz> (cherry picked from commit 01b6174) (cherry picked from commit 89e4ce2e1e7f9f17a1d752c05fd0a166ca0b8be1) * fix(desktop): keep managed agent avatars usable across communities (block#7732) ## Summary Fix Desktop-managed agents losing avatars after joining another community. The saved persona/instance stores one desired source URL, but authenticated media belongs to a community. Republishing community A’s URL in B does not make that image accessible in B. - Before comparing or publishing kind-0, reuse or copy configured-community avatar media into the pinned destination. Keep exact image bytes, including animation, and leave the saved source unchanged. - Keep the previous profile on transfer/publication failure so existing reconciliation can retry. Reuse destination bytes during source outages; after source removal, reuse only an already-published signed destination picture with the same original content hash. - Refresh source permissions through narrow, serialized IPC without reconnecting or resetting the active community. Startup waits until the latest permission update completes before restoring agents. ### Related issue Refs block#2366 (avatar portion only, not runtime availability). Related block#2659 deliberately excludes avatar/media mirroring; this change addresses that media boundary without introducing runtime fan-out. Searched existing issues/PRs for `avatar community` before opening. ## Contract and safety Desktop-managed profiles still follow the saved persona source; this does not introduce community-specific persona editing. Agent-managed profiles still opt out of automatic reconciliation. Authenticated transfer requests use the fixed agent key/NIP-OA tag, origin-scoped Blossom auth, and the pinned destination, never mutable active-workspace owner credentials. They refuse redirects, enforce 30-second request timeouts and bounded bodies, verify SHA-256 and detected image MIME, and validate the returned descriptor’s origin/hash/MIME/size. Source permission is rechecked after an awaited destination HEAD miss. The removed-source fallback verifies the destination profile’s signature, kind, signer, origin, and original-media hash without contacting the removed source. Ordinary external images retain passthrough behavior and receive no community credentials. No public-media policy change, image-reader URL rewriting, new persistence, background service, or global profile merge. ## Testing Normal commit and push hooks passed on exact head `925781eece66d5f6a4b084b3b3abd6309cda357e`; working tree clean before and after: - Desktop formatting/lint, typecheck, and **6,508 tests**. - Full native workspace suite, including **3,204 desktop-library tests** (19 ignored), integration tests, terminal crates, and doc tests. - Native Clippy on default and `mesh-llm` workspace/all-target configurations with warnings denied; repository file-size and branch-skew gates. Regression coverage exercises production reconciliation/shared publication against two local HTTP fixtures: add/edit/restart, upload and rejected-publication retry, removed-source local-image preservation, fixed signer/destination, redirect/hash/descriptor/size failures, revocation during pending HEAD, and opt-out no-I/O. Mounted initialization tests cover inactive-list changes without workspace reset, two deferred permission updates, and rejection of either update. Earlier working-tree validation at base `4ab4f786085a23fe6126529861840eff6048ceee` also passed the E2E frontend build and four Chromium mock-bridge community flows (boot, switch, active removal/fallback, leave-final/setup). Subsequent production changes only serialize permission writes and wait for a stable latest update; the complete suites above cover the final commit. Mutations independently removing serialization or stable waiting failed the new regression. ## Remaining validation and limitations - No live production avatar was changed. Native GUI rendering against two hosted communities remains to be verified with a build containing this fix; local HTTP fixtures and the browser mock bridge do not establish that outcome. - A removed/unreachable source with no existing destination copy cannot be recovered automatically. Choose a reachable image/source in that case. - This is a publication/reconciliation fix, not a visual redesign. No before/after native GUI screenshots are claimed. Broad repository CI remains required; it was not duplicated locally. ## Review Independent security/scope and caller/ordering reviews are clear after the bounded fixes. Final hook-ordering reread covered the staged tree that became this commit. Commit authorship/sign-off use the implementing Carl identity and configured Carl signing key. Signed-off-by: Carl <32a2e2c9d428ee08902cab75d956da2c1d235a22d4766b0dd4138bf6e2e5db1d@buzz.block.builderlab.xyz> Co-authored-by: Carl <32a2e2c9d428ee08902cab75d956da2c1d235a22d4766b0dd4138bf6e2e5db1d@buzz.block.builderlab.xyz> (cherry picked from commit 8953cbf) (cherry picked from commit f0a773ed029a9f0f751a1231642a442b26137590) * feat(db): expose connection setup metrics (block#7286) ## Why Current pool metrics show checkout outcomes and pool state after the fact. They do not show when a checkout began or which writer-connection setup step failed. During startup and pool growth, operators need to distinguish pool saturation from a slow or unsafe connection setup. ## What - Count every instrumented database checkout when it starts, with a fixed operation label. - Measure writer-pool creation and every physical writer connection across `physical_connect`, `created_at_floor`, `session_timeouts`, `isolation`, and `ready`. - Publish fixed-cardinality start, terminal-outcome, duration, and waiter metrics without database URLs, SQL, raw errors, connection ordinals, or per-connection lifecycle logs. - Document how to identify the active setup bottleneck during a rollout or incident. ## How The existing typed checkout wrapper records starts and current waiters. The huddle-history path uses that wrapper instead of a raw pool checkout. The production SQLx `after_connect` hook records start, terminal outcome, and duration for each sequential safety step. A drop guard records cancellation once if setup exits before a terminal result. Fixed enums keep every label bounded. For a phase, `started_total - sum(attempts_total)` is the number of in-progress attempts on that pod. Because setup is sequential, a later phase starting also proves the earlier phases succeeded. The database path emits metrics only. This PR intentionally does not add per-connection lifecycle receipts or direct `stderr` writes. ## Risk Medium. This changes the production writer-pool `after_connect` hook and adds one counter update to instrumented checkout paths. It does not change the database safety statements or their failure behavior. The telemetry has fixed labels and no per-connection identifiers. ## Testing At exact head `ffb5fcb114a9c986ffd0cb2cfc9d413a32d5b1aa`, a release relay was started against isolated local PostgreSQL, Redis, and MinIO services. The main health endpoint returned `ok`, readiness returned `{"status":"ready"}`, the connection-step metrics were exported, and no database lifecycle receipts were emitted. A release CLI then created a channel, sent a message, and read the same event back successfully. The earlier staging deployment used pre-rebase head `f58e9480a4f068db0c591f604fd6800fdd4bfc45`. The deployed multi-architecture image came from [GitHub Actions run 33780888255](https://github.com/block/buzz/actions/runs/33780888255), manifest `sha256:b9351fa644e08376cbe1999f9bee311d33d1799a68eadef7929c4f862a832fec`. The [staging deployment](https://github.com/squareup/builderbot-platform-core-infrastructure/pull/317) brought both pods in ReplicaSet `buzz-6c8758bd7d` to Ready with zero restarts, and the connection metrics produced data in the [rollout dashboard](https://app.datadoghq.com/dashboard/tm7-qxr-wt2/buzz-startup--rollout-safety). That staging image predates the cleanup that removed per-connection lifecycle logs; the metric schema and database safety statements are unchanged. ## Verification - `cargo test -p buzz-db -- --test-threads=1`: 128 tests passed across the package and integration target; 255 opt-in tests remained ignored. - Four opt-in production-path PostgreSQL regressions passed: initial minimum connections, post-startup pool growth, isolation failure, and session-timeout setup failure. - All 35 relay media tests passed against the isolated PostgreSQL database. - Rust formatting, workspace clippy, desktop/Tauri clippy, web checks, mobile analysis, security checks, and file-size policy checks passed. - The repository-wide unit stage also exposed three unrelated existing `buzz-acp` failures: one timing-sensitive keepalive test and two environment-default tests. ## Bigger picture This is the database-metrics part of the startup and rollout observability work. The early-startup lifecycle foundation merged in block#7258, so the rebase removed that duplicate commit from this PR. Originating discussion: buzz://message?channel=6ac85131-70cd-4bda-a031-38d34114934e&id=fa2bed181c092697210a60bb6eedc55a665d5c1c6cabc1413a04686647011f71 Generated with Codex --------- Signed-off-by: Ravneet Arora <rarora@squareup.com> (cherry picked from commit 4c2086c) (cherry picked from commit b42caabf790ac13f4507e97936243959934ae461) * test(db): freeze migration 46 against the upstream 0046 collision Upstream block/buzz shipped 0046_storage_acco...[truncated] (cherry picked from commit 6bc8126296473332be78cb31a9f95c313165d6ac) --------- Signed-off-by: Tom Brow <tomb@block.xyz> Signed-off-by: Carl <32a2e2c9d428ee08902cab75d956da2c1d235a22d4766b0dd4138bf6e2e5db1d@buzz.block.builderlab.xyz> Signed-off-by: Ravneet Arora <rarora@squareup.com> Co-authored-by: Tom Brow <tomb@block.xyz> Co-authored-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Carl <32a2e2c9d428ee08902cab75d956da2c1d235a22d4766b0dd4138bf6e2e5db1d@buzz.block.builderlab.xyz> Co-authored-by: ravarora2 <130506156+ravarora2@users.noreply.github.com>
Why
Early relay failures can currently appear as a container restart without a trustworthy in-process account of whether crypto, structured logging, configuration, relay identity, or the metrics listener failed. Most of those steps happen before the Prometheus exporter exists, so their chronology belongs in logs rather than metrics.
Implements the logs-only early-startup slice of #7238. Post-bind Prometheus exporter supervision is tracked separately in #7284.
What changed
crypto_init,tracing_init,config_load,key_load,metrics_bind, and the aggregateprocess_telemetryphase;metrics_bindcan be classified without logging raw values, while preserving the existing publicmetrics::installAPI;This PR adds no startup metric families and no dashboard contract. Existing application metrics remain unchanged.
Verification
Exact head:
8faf7526822a119efa035e58b2b3c59aa67fc81dcargo fmt --all -- --checkcargo clippy -p buzz-relay --all-targets -- -D warningscrates/buzz-relay/src/api/media.rs:1145withSqlx(PoolTimedOut)because local PostgreSQL is unavailableAll exact-head GitHub CI gates are green, including lint, unit tests, PostgreSQL, relay/backend/desktop integration, both Linux server cross-compiles, Windows/macOS builds, and security checks.
Staging verification
dev-sha-8faf7526822a119efa035e58b2b3c59aa67fc81d-run-33708188952-1sha256:26cad28266a6bb0b0e7081eb6091d374e5489f8bb78c475a4a65737dee86cc67buzz-d68764bc7has two Ready pods with zero restartsprocess_telemetry/terminal/succeededat 3 msbuzz_startup_phase_terminalorbuzz_startup_phase_duration_secondsfamiliesThe experimental Row 7 was removed from the Buzz Startup & Rollout Safety dashboard. This logs-only PR deliberately adds no replacement dashboard row.
Update Sep 3, 12:26 ET: Clarified the review boundary: this PR does not close the broader #7238. Later exporter-task termination is pre-existing runtime behavior and is now explicitly tracked in #7284; no production code or staged image changed in this update.
Generated with Codex