Skip to content

feat(admin): add operations console for mirrors, workers, and NIP-05 - #895

Merged
cameri merged 7 commits into
mainfrom
feat/admin-operations
Oct 20, 2026
Merged

cameri merged 7 commits into
mainfrom
feat/admin-operations

Conversation

@Ferryx349

Copy link
Copy Markdown
Collaborator

Description

This PR adds an Operations area to the admin console so operators can inspect mirroring, background workers, NIP-05 verification state, and storage counts without SSH or direct DB access.

  • Publish mirror and worker heartbeats to Redis (TTL) from static-mirror, maintenance, and relay-monitor workers
  • Admin APIs: GET /admin/ops/mirrors, GET /admin/ops/workers, GET /admin/ops/nip05-verifications, POST /admin/ops/nip05-verifications/:pubkey/reverify, GET /admin/ops/storage/counts
  • Dashboard Operations nav with mirror health, worker ops, Tor/I2P probe panel (network health moved here), NIP-05 list with filters/reverify, and NIP-45-style storage counts
  • Same admin auth, IP allowlisting, and rate limiting as existing admin routes

Related Issue

Closes :- #816

Motivation and Context

How Has This Been Tested?

Screenshots (if appropriate):

Types of changes

  • Non-functional change (docs, style, minor refactor)
  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist:

  • My code follows the code style of this project.
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I have read the CONTRIBUTING document.
  • I have added tests to cover my code changes.
  • I added a changeset, or this is docs-only and I added an empty changeset.
  • All new and existing tests passed.

Avoid Redis client creation in worker factories when cache is unavailable, and flush async probe work under fake timers after ops heartbeats.
@changeset-bot

changeset-bot Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: d145049

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
nostream Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@greptile-apps

greptile-apps Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge, with a non-blocking display issue in storage counts.

Findings

  1. P2 Storage counts hide their range ▶

Summary

Adds an Operations area to the admin console.

  • The Operations page brings mirror and worker health together with network probes.
  • NIP-05 records can be searched and checked again from Operations.
  • Operators can count stored events by kind and time window.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  Workers["Mirror, maintenance, and monitor workers"] --> Redis["Redis heartbeats"]
  Redis --> APIs["Admin Operations APIs"]
  DB["Event and NIP-05 records"] --> APIs
  APIs --> UI["Operations page"]
  UI --> Reverify["Reverify NIP-05"]
  Reverify --> DB
Loading

Reviews (6) · Last reviewed commit: "Merge branch 'main' into feat/admin-oper..." · Reviewed by Greptile

Comment thread resources/admin/assets/admin-ops.js Outdated
Comment thread src/app/maintenance-worker.ts
Comment thread src/utils/admin-ops-store.ts
Comment thread src/utils/admin-ops-config.ts
Comment thread resources/admin/assets/admin-ops.js Outdated
Comment thread src/controllers/admin/get-ops-storage-counts-controller.ts Outdated
Comment thread resources/admin/assets/admin-ops.js
Comment thread src/controllers/admin/get-ops-storage-counts-controller.ts Outdated
Escape untrusted ops UI data, stop polling on logout, add NIP-05 paging,
and make heartbeats/storage counts safer without blocking worker jobs.
Comment thread src/app/relay-monitor-worker.ts Outdated
Comment thread src/utils/nip05-reverification.ts
@coveralls

coveralls commented Oct 19, 2026 •

Copy link
Copy Markdown
Collaborator

Coverage Report for CI Build 39290635299

Warning

No base build found for commit 02b70cf on main.
Coverage changes can't be calculated without a base build.
If a base build is processing, this comment will update automatically when it completes.

Coverage: 73.787%

Details

  • Patch coverage: 212 uncovered changes across 22 files (221 of 433 lines covered, 51.04%).

Uncovered Changes

Top 10 Files by Coverage Impact Changed Covered %
src/utils/admin-ops-store.ts 84 40 47.62%
src/app/static-mirroring-worker.ts 37 0 0.0%
src/controllers/admin/get-ops-nip05-verifications-controller.ts 32 6 18.75%
src/controllers/admin/post-ops-nip05-reverify-controller.ts 24 5 20.83%
src/utils/admin-ops-config.ts 38 19 50.0%
src/repositories/nip05-verification-repository.ts 17 0 0.0%
src/controllers/admin/get-ops-storage-counts-controller.ts 21 5 23.81%
src/controllers/admin/get-ops-mirrors-controller.ts 8 3 37.5%
src/controllers/admin/get-ops-workers-controller.ts 8 3 37.5%
src/utils/admin-ops-storage.ts 37 32 86.49%
Total (26 files) 433 221 51.04%

Coverage Regressions

Requires a base build to compare against. How to fix this →


Coverage Stats

Coverage Status
Relevant Lines: 11072
Covered Lines: 8571
Line Coverage: 77.41%
Relevant Branches: 5786
Covered Branches: 3868
Branch Coverage: 66.85%
Branches in Coverage %: Yes
Coverage Strength: 812.45 hits per line

💛 - Coveralls

@chappie-daemon chappie-daemon left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: approve-level

Verified independently on the PR head: the 5 new admin-ops unit tests and the 8 relay-monitor worker tests pass, tsc reports no errors in any PR file, and biome lint (what CI runs) is clean on all new/changed source. The security-relevant claims check out: all five routes carry the identical adminRateLimitMiddleware + adminAuthMiddleware chain as their neighbors, the storage-counts query parser bounds kinds (max 20, 0–65535) and the time window (90 days) with a 5s statement timeout mapped to 504, the NIP-05 list clamps limit/offset and validates minFailures with everything parameterized through knex, the reverify endpoint schema-validates the pubkey and 404s/409s/502s correctly, and the dashboard escapes every interpolated value (escapeHtml/escapeAttr on the user-controlled NIP-05 identifiers included) — the #110 lesson visibly applied. Heartbeat publishing is properly failure-isolated so a Redis outage can't break the workers, intervals are cleared on exit, and the changeset is included this time.

One finding that should be fixed before merge:

The relay-monitor keepalive wipes the per-run heartbeat. publishWorker does a full setKey overwrite, and the 45s keepalive publishes a bare {type: 'relay-monitor', updatedAt} — no lastError, no run timestamps. So a failed probe run's lastError vanishes from the Operations dashboard within 45 seconds even though nothing recovered; the panel reads "Live" with no error until the next probe run, which may be far later. The static-mirroring worker doesn't have this problem (its keepalive republishes full state). The keepalive should re-publish the last known partial (kept in a member field) rather than a bare liveness ping — otherwise the worker-ops panel misleads exactly when it matters.

Further notes, non-blocking:

  • Thin tests for the size. 1725 additions, 5 new unit tests. The pure functions (sanitizeAdminOpsError, isAdminOpsHeartbeatStale, mergeMirrorRows/mergeWorkerRows) and the store's TTL/index logic are all easily unit-testable — and the keepalive finding above is exactly the class of bug a store-level test would have caught.
  • The heartbeat index set never shrinks. Dead mirror:<index> / worker:<type>:<index> members are skipped on read (missing key → continue), but they accumulate in admin:ops:heartbeat:index forever — a slow Redis memory leak across config changes over a relay's lifetime.
  • getAdminOpsStore is a singleton that silently ignores the cache argument on later calls. Fine with one cache per process, but an assertion would be cheaper than a future mystery.
  • Template checklist unfilled again — though the changeset made it in this time, which is the part that matters.

@chappie-daemon chappie-daemon left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review update: requesting changes

Revising my verdict on this one — approve was the wrong call, and the coverage question is what settled it.

The keepalive clobber isn't a polish nit: it silently wipes a failed probe run's error from the Operations dashboard within 45 seconds, which is the exact opposite of what this feature is for. And the new tests cover ~15% of the new backend code (zero on the store, the controllers, the sanitizer, the config merge) — so nothing in the PR's own suite guards the fix or would catch the next bug of the same shape. A real functional bug in the feature's core purpose, plus tests that can't see it, is request-changes.

What flips it back: have the relay-monitor keepalive re-publish the last known heartbeat state instead of a bare liveness ping, and add store-level tests covering publish/list/stale behavior plus the keepalive interaction. The pure functions (sanitizeAdminOpsError, mergeMirrorRows/mergeWorkerRows, isAdminOpsHeartbeatStale) are cheap to cover and would have caught this class of bug.

Keepalive republishes last probe worker state, reverify upserts only when
the stored nip05 still matches, and add ops console unit tests.
Comment thread src/app/relay-monitor-worker.ts
Successful probe runs reset stored ops heartbeat errors while keepalives
still refresh updatedAt from the last known run state.
Comment thread test/unit/app/relay-monitor-worker.spec.ts
Recreate the ops store when the cache instance changes so relay monitor
unit tests do not share heartbeat mocks across cases.
@Ferryx349
Ferryx349 force-pushed the feat/admin-operations branch from a3fe2b3 to 6db1664 Compare October 19, 2026 16:58
showStorageError(payload.error ?? 'Storage count query failed')
return
}
renderStorage(payload.counts)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Storage counts hide their range

With Since and Until left blank, the server counts only the last 24 hours. This page discards the returned since and until values and shows an unlabeled storage count, so operators can mistake it for the total stored events. Show the effective date range beside the results, or make the 24-hour default visible before running the query.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@cameri
cameri merged commit a52cbfd into main Oct 20, 2026
17 checks passed
@cameri
cameri deleted the feat/admin-operations branch October 20, 2026 11:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants