mirror of
https://github.com/Kpa-clawbot/meshcore-analyzer.git
synced 2026-05-23 09:25:07 +00:00
ba6c2ac6ba
## Summary
- **Backend**: adds `relayTimes` in-memory index (sorted unix-millis per
repeater pubkey), maintained in lockstep with `byPathHop`. Populated at
startup from all packet observations (not just best), updated on
ingest/evict/backfill. Exposes `relay_count_1h`, `relay_count_24h`,
`last_relayed` in both `/api/nodes` (for repeaters) and
`/api/nodes/{pubkey}/health`.
- **Frontend**: `getNodeStatus` extended to three-state (`relaying` /
`active` / `stale`) for repeaters based on relay_count_24h.
`getStatusInfo` is the single source of truth for status label,
explanation, and relay stats. Detail pane shows relay counts and last
relayed time. Nodes list gets a status emoji column with hover tooltip
showing relay info.
- **Correctness fixes**: relay index scans all observations per packet
(not just best); backfill now updates relay index after resolving paths;
pubkeys lowercased consistently throughout index.
## Changes
### `cmd/server/store.go`
- `relayTimes map[string][]int64` field added to `PacketStore`
- `addTxToRelayTimeIndex` / `removeFromRelayTimeIndex`: scan all
observations, idempotent sorted insert, lowercase keys
- `relayMetrics(times, nowMs)`: returns `(count1h, count24h,
lastRelayed)`
- `buildPathHopIndex`: populates `relayTimes` at startup
- `pollAndMerge`: updates relay index on ingest and eviction; new `else`
branch for path-unchanged observations
- `addTxToPathHopIndex` / `removeTxFromPathHopIndex`: lowercase resolved
pubkeys (fixes casing mismatch with lookup)
### `cmd/server/routes.go`
- `GetBulkHealth` / `GetNodeHealth`: include relay stats for repeater
nodes
- `handleNodes`: enriches repeater nodes with relay stats from
`relayTimes` so list view has same data as detail pane
### `cmd/server/neighbor_persist.go`
- `backfillResolvedPathsAsync`: calls `addTxToRelayTimeIndex` after
`pickBestObservation` to capture newly resolved pubkeys
### `public/roles.js`
- `getNodeStatus(role, lastSeenMs, relayCount24h)`: three-state logic
for repeaters
- `getStatusInfo(n)`: single source of truth returning status, label,
explanation, relay counts, last relayed
### `public/nodes.js`
- Detail pane: `n.stats` populated from health endpoint before
`getStatusInfo` call
- Nodes list: status emoji column with relay hover tooltip; status
filter uses `getStatusInfo`
### Tests
- `relay_liveness_test.go`: index functions, relay metrics, wiring
integration, bulk/single health endpoints
- `test-repeater-liveness.js`: three-state frontend logic, backward
compat
## Test plan
- [x] Repeater with recent relay traffic shows green relaying emoji in
list and detail pane
- [x] Repeater with no relay traffic in 24h shows yellow idle in both
views
- [x] Repeater not heard recently shows grey stale in both views
- [x] Non-repeater nodes unaffected (no relay stats, no status change)
- [x] Hover tooltip on list emoji shows relay count and last relayed
time
- [x] `go test ./...` passes
- [x] `node test-repeater-liveness.js` passes
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: openclaw-bot <bot@openclaw.local>
91 lines
3.2 KiB
Docker
91 lines
3.2 KiB
Docker
# Build stage always runs natively on the builder's arch ($BUILDPLATFORM)
|
|
# and cross-compiles to $TARGETOS/$TARGETARCH via Go toolchain. No QEMU.
|
|
# BUILDPLATFORM is auto-set by buildx; default to linux/amd64 so plain
|
|
# `docker build` (without buildx) doesn't fail on an empty platform string.
|
|
ARG BUILDPLATFORM=linux/amd64
|
|
FROM --platform=$BUILDPLATFORM golang:1.22-alpine AS builder
|
|
|
|
ARG APP_VERSION=unknown
|
|
ARG GIT_COMMIT=unknown
|
|
ARG BUILD_TIME=unknown
|
|
# Provided by buildx for multi-arch builds
|
|
ARG TARGETOS
|
|
ARG TARGETARCH
|
|
|
|
# Build server (pure-Go sqlite — no CGO needed, cross-compiles cleanly)
|
|
WORKDIR /build/server
|
|
COPY cmd/server/go.mod cmd/server/go.sum ./
|
|
COPY internal/geofilter/ ../../internal/geofilter/
|
|
COPY internal/sigvalidate/ ../../internal/sigvalidate/
|
|
COPY internal/packetpath/ ../../internal/packetpath/
|
|
COPY internal/dbconfig/ ../../internal/dbconfig/
|
|
COPY internal/dbschema/ ../../internal/dbschema/
|
|
COPY internal/perfio/ ../../internal/perfio/
|
|
RUN go mod download
|
|
COPY cmd/server/ ./
|
|
RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} \
|
|
go build -ldflags "-X main.Version=${APP_VERSION} -X main.Commit=${GIT_COMMIT} -X main.BuildTime=${BUILD_TIME}" -o /corescope-server .
|
|
|
|
# Build ingestor
|
|
WORKDIR /build/ingestor
|
|
COPY cmd/ingestor/go.mod cmd/ingestor/go.sum ./
|
|
COPY internal/geofilter/ ../../internal/geofilter/
|
|
COPY internal/sigvalidate/ ../../internal/sigvalidate/
|
|
COPY internal/packetpath/ ../../internal/packetpath/
|
|
COPY internal/dbconfig/ ../../internal/dbconfig/
|
|
COPY internal/dbschema/ ../../internal/dbschema/
|
|
COPY internal/perfio/ ../../internal/perfio/
|
|
RUN go mod download
|
|
COPY cmd/ingestor/ ./
|
|
RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} \
|
|
go build -o /corescope-ingestor .
|
|
|
|
# Build decrypt CLI
|
|
WORKDIR /build/decrypt
|
|
COPY cmd/decrypt/go.mod cmd/decrypt/go.sum ./
|
|
COPY internal/channel/ ../../internal/channel/
|
|
RUN go mod download
|
|
COPY cmd/decrypt/ ./
|
|
RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} \
|
|
go build -ldflags="-s -w" -o /corescope-decrypt .
|
|
|
|
# Runtime image
|
|
FROM alpine:3.20
|
|
|
|
RUN apk add --no-cache mosquitto mosquitto-clients supervisor caddy wget
|
|
|
|
WORKDIR /app
|
|
|
|
# Go binaries
|
|
COPY --from=builder /corescope-server /corescope-ingestor /corescope-decrypt /app/
|
|
|
|
# Frontend assets + config
|
|
COPY public/ ./public/
|
|
COPY config.example.json channel-rainbow.json ./
|
|
|
|
# Bake git commit SHA — manage.sh and CI write .git-commit before build
|
|
# Default to "unknown" if not provided
|
|
RUN echo "unknown" > .git-commit
|
|
|
|
# Supervisor + Mosquitto + Caddy config
|
|
COPY docker/supervisord-go.conf /etc/supervisor/conf.d/supervisord.conf
|
|
COPY docker/supervisord-go-no-mosquitto.conf /etc/supervisor/conf.d/supervisord-no-mosquitto.conf
|
|
COPY docker/supervisord-go-no-caddy.conf /etc/supervisor/conf.d/supervisord-no-caddy.conf
|
|
COPY docker/supervisord-go-no-mosquitto-no-caddy.conf /etc/supervisor/conf.d/supervisord-no-mosquitto-no-caddy.conf
|
|
COPY docker/mosquitto.conf /etc/mosquitto/mosquitto.conf
|
|
COPY docker/Caddyfile /etc/caddy/Caddyfile
|
|
|
|
# Data directory
|
|
RUN mkdir -p /app/data /var/lib/mosquitto /data/caddy && \
|
|
chown -R mosquitto:mosquitto /var/lib/mosquitto
|
|
|
|
# Entrypoint
|
|
COPY docker/entrypoint-go.sh /entrypoint.sh
|
|
RUN chmod +x /entrypoint.sh
|
|
|
|
EXPOSE 80 443 1883
|
|
|
|
VOLUME ["/app/data", "/data/caddy"]
|
|
|
|
ENTRYPOINT ["/entrypoint.sh"]
|