Documentation
¶
Overview ¶
Package clirewardsserver cmd/skywire-cli/commands/rewards/server/cmd.go c4-vis-cli
Package clirewardsserver cmd/skywire-cli/commands/rewards/server/favicon.go c4-vis-cli
Package clirewardsserver cmd/skywire-cli/commands/rewards/server/fonts.go c4-vis-cli
Package clirewardsserver cmd/skywire-cli/commands/rewards/server/htmpl.go c4-vis-cli
Package clirewardsserver cmd/skywire-cli/commands/rewards/server/liveness.go c4-vis-cli
Package clirewardsserver cmd/skywire-cli/commands/rewards/server/logging.go c4-vis-cli
Package clirewardsserver cmd/skywire-cli/commands/rewards/server/login.go c4-vis-cli
Package clirewardsserver cmd/skywire-cli/commands/rewards/server/loginchain.go c4-vis-cli
Package clirewardsserver cmd/skywire-cli/commands/rewards/server/logo.go c4-vis-cli
Package clirewardsserver cmd/skywire-cli/commands/rewards/server/nodeproxy.go c4-vis-cli
Package clirewardsserver cmd/skywire-cli/commands/rewards/server/seo.go c4-vis-cli pages.
Crawlers index per-page metadata better when the title + description carry the actual reward numbers (visor count, total SKY, country count) instead of templated stubs, and the schema.org Dataset JSON-LD block gives Google et al a machine-readable rich-snippet target. All inputs come from the same hist/<date>_stats.txt the /skycoin-rewards/hist/:date route already reads to render the page, so there's no extra disk hit and no risk of metadata diverging from rendered content.
Package clirewardsserver cmd/skywire-cli/commands/rewards/server/server.go c4-vis-cli
Package clirewardsserver cmd/skywire-cli/commands/rewards/server/stats.go c4-vis-cli
Package clirewardsserver cmd/skywire-cli/commands/rewards/server/stats_tui.go c5-reward-server
Terminal rendering of the network statistics, exported to HTML.
There is ONE renderer here and it emits ANSI. Printed to a terminal it is the TUI; passed through ansifilter it is the HTML the reward server serves. That is deliberate: the reward UI has always looked like a terminal, and for a long time it literally was one — bash wrapped in a Go web server, whose output was already terminal-formatted and simply passed through. This restores that property on purpose instead of hand-writing HTML that resembles it, and it means the eventual TUI build of this page is the same code with a different sink.
Libraries: asciigraph for the plots (already a dependency, and its SeriesColors/AxisColor options emit the SGR sequences ansifilter reads), ansifilter-go for the ANSI->HTML conversion, lolcat-go for the banner.
Package clirewardsserver cmd/skywire-cli/commands/rewards/server/stats_tui_arch.go c5-reward-server
The fleet's CPU architectures, as a pie.
WHERE THE DATA COMES FROM, AND WHERE IT DOES NOT.
It does not come from TPD. TPD's visor heartbeat carries one free-form field — the version string — and nothing else; there is no arch field in store.VisorSummary, in the /version or /versions endpoints, or in any of the three CXO stats leaves. An architecture breakdown cannot be sourced from the transport discovery today without first plumbing arch through RecordHeartbeat, which is a TPD protocol change and not this panel's job.
It comes from the operator surveys the reward server already holds: each visor's node-info.json carries `go_arch`, and the frequency table built from it is what /stats has been rendering as an HTML pie since before this terminal panel existed. This is the same data, same parse (ParseFrequencyStats), drawn where the rest of the page now lives — so the two renderings cannot disagree.
That provenance is stated on the panel rather than left implied, because the two populations genuinely differ: the survey set is visors that submitted a survey, which is a subset of the visors TPD counts. A percentage over the wrong denominator is exactly the kind of confident wrong number the rest of this file's conventions exist to prevent.
WHY THE PIE IS DRAWN HERE AND NOT BY A LIBRARY.
Nothing vendored draws one. asciigraph plots series; 0magnet/plot-go is a stream-processing pipeline with no chart types; pterm has bar, heatmap and tree printers and no pie. Adding a dependency for one disc is worse than rasterizing it: the disc below is a cell-by-cell angle test, which is a dozen lines and has no version to track.
Package clirewardsserver cmd/skywire-cli/commands/rewards/server/stats_tui_coverage.go c5-reward-server
Service reachability across the dmsg servers.
The deployment services deliberately do NOT register client entries in dmsg-discovery — an entry lookup for TPD or SD returns 404. That is the design: a service should not cost every caller a discovery round-trip, it should be equally reachable through whichever dmsg server the caller already holds a session with. Callers reach them through seeded delegated-server entries instead.
That design has a failure mode nothing was checking. If a service is connected to only some of the dmsg servers, it stays perfectly reachable for clients on those servers and quietly unreachable for the rest — no error anywhere, just a subset of the network unable to resolve it. Since the service publishes no entry, discovery cannot report the gap either.
Each service's /health lists the dmsg servers it currently holds sessions with. Comparing that against the registered server set turns an invisible partial connection into a number.
Package clirewardsserver cmd/skywire-cli/commands/rewards/server/stats_tui_data.go c5-reward-server
Data gathering for the terminal-rendered statistics panel.
Every source is fetched independently and every failure is recorded rather than returned: the page renders whatever succeeded and names what did not. That matters here more than usual, because the previous version reduced a 24 MB per-transport body to produce three numbers and, when that fetch died with EOF, the whole summary rendered as an error string.
Each source is read from TPD's CXO feeds first and over HTTP-over-dmsg only on a miss — see statscxo.go for why that is a fix and not a refactor. The panel records WHICH of the two answered, alongside the failures, because a snapshot minutes old and a fetch made just now are different claims about the network and a panel that printed them identically would be lying by omission in the same way a rendered zero is.
Package clirewardsserver cmd/skywire-cli/commands/rewards/server/stats_tui_dmsg.go c5-reward-server
The dmsg layer, in the terminal panel.
Nothing on the reward site showed dmsg at all: the transport and uptime views describe what the network carries, and none of it describes the substrate carrying it. A saturated dmsg server is invisible in every existing chart, yet it silently pushes clients elsewhere.
Package clirewardsserver cmd/skywire-cli/commands/rewards/server/stats_tui_pervisor.go c5-reward-server
How many transports each visor holds, as a histogram.
The network panel says how many transports exist and how many visors exist; their ratio is an average that describes nobody. The shape that matters is the distribution: a network where every visor holds three transports and one where a tenth of the visors hold thirty and the rest hold one produce the same average and are not the same network.
WHY THIS PANEL IS GATED AND THE OTHERS ARE NOT.
TPD's transport index oscillates. Adjacent samples of the same aggregate have been observed at 14,162 and 30,538 total edges (skycoin/skywire #4513) — the index is readable while it is still refilling after a restart, and nothing in the HTTP body distinguished a refill from the network having halved. A line chart of a fluctuating total at least shows the fluctuation. A histogram does not: it renders one sample as a static shape, and a sample taken mid-refill draws a network whose visors each hold half the transports they hold. That is an artifact presented as a finding, which is worse than no panel.
So this panel refuses to draw unless the sample is vouched for, on three independent counts:
- store-level: TransportSummary.Partial / MissingBatches (#4542) — a batched read over the index whose batches did not all return.
- publisher-level: the stats feed's complete / confidence stamp (#4526), the trailing-peak verdict on whether the total looks settled or is refilling.
- self-consistency: every transport contributes exactly two edges, so the per-key index must sum to twice the aggregate's total.
Any of the three failing replaces the histogram with a named absence carrying the reason, the same convention tuiMissing uses for a fetch that died.
WHAT THE THIRD CHECK IS ALLOWED TO COMPARE. It first ran the per-key sum against the aggregate carried on the CXO stats feed, and fired constantly — 13,298 edges against 10,892 transports, 39% apart, with the per-key index blamed for losing them. It was not. Both endpoints are reductions of ONE cached slice in TPD, and read off one snapshot they agree to the byte: paired fetches measured 17,982/8,991, 17,754/8,877 and 21,644/10,822, exactly 2:1 every time, over dmsg and on the host alike. What differed was WHEN. The feed's aggregate is deliberately biased late and high — the publisher holds the last complete sample for up to five minutes rather than republish one that looks like a refill — while the per-key body is fetched live, and the network total moves 2.4x inside forty seconds (measured again for skywire#4513). Comparing those two is comparing two moments, and the difference between two moments is not evidence about either.
So the check now runs against the transport total of the snapshot the per-key body was ITSELF reduced from, which TPD stamps on that same response. That comparison has no time in it: one snapshot disagreeing with itself is a real defect and nothing else. When the header is absent the check does not run, because an unverifiable comparison is worth less than none — the first two checks still stand, and a false accusation against the index is the failure this panel was built to avoid.
Package clirewardsserver cmd/skywire-cli/commands/rewards/server/stats_tui_sn.go c5-reward-server
The route setup nodes, in the terminal panel.
Every route in the network is negotiated by a route setup node, and until now nothing on the reward site said anything about them. The transport and uptime views describe what the network is made of; the setup nodes describe whether a route across it can actually be established. A setup node refusing nearly every request — circuit breakers open, destinations unreachable — leaves no mark on any existing chart: the transports are still registered, the visors are still online, and routing is simply not working.
The nodes are enumerated from the deployment's route_setup_nodes list rather than hardcoded (that list is NOT transport_setup, which is a different set of keys for a different job), and a node that does not answer is drawn as an explicit unreachable row. Silently omitting it would make a dead setup node look like a deployment that has one fewer.
Package clirewardsserver cmd/skywire-cli/commands/rewards/server/stats_tui_uptime.go c5-reward-server
`skywire cli ut tpd graph`, folded into the statistics panel.
The command already renders the uptime tracker's v3 per-5-minute bitmaps as shaded hourly blocks, one bar per visor. That rendering existed only at a terminal; the site showed the same tracker data summed into a single visors-online line, which says how many were up and never which ones. A fleet holding steady at 880 online looks identical whether the same 880 visors are up all week or a churning 1,200 take turns, and only the per-visor bars tell those apart.
TWO THINGS ARE DELIBERATE HERE.
It does not shell out. The reward server's liveness chart ran `skywire cli ut tpd graph --json` as a subprocess. This file reads the SAME sources that command's fetch chain reads, in-process: the tpd-uptime CXO feed first (clirpc maps /uptimes?v=v3 onto exactly the uptimes/days/<n> leaf read below), then HTTP over dmsg. It cannot literally call clirpc — pkg/visor imports this package, so importing clirpc here is an import cycle — but it reads the same leaf and the same endpoint, and fetchLivenessSeries now shares the result, so the ~2 MB dump is pulled once for both renderings rather than once per subprocess.
The glyphs are not re-implemented. ShadeForCount / RollingBar were hoisted into pkg/uptimestats (they had already been copy-pasted once, into cmd/skywire-cli/commands/visor/info.go), so the bars in this panel are byte-for-byte what the CLI prints.
Package clirewardsserver cmd/skywire-cli/commands/rewards/server/statscxo.go c5-reward-server
CXO as the first source for the statistics pages, with HTTP-over-dmsg kept behind it.
WHY. The statistics pages fetched everything from TPD over HTTP-over-dmsg, and those fetches fail intermittently with EOF — observed live on /stats and /stats/charts for bodies as small as 2.7 KB, and for a /metric/visor/{pks} URL ~1,400 characters long. It is not size. The reward server's long-lived dmsg client periodically loses its sessions ("no mux session available for ping", "session closed"; skycoin/skywire#4538) and every request issued inside that window dies outright, because an HTTP fetch is all-or-nothing AT REQUEST TIME.
A CXO subscriber has no such exposure. It holds a local snapshot that was filled in the background, so a read is served from memory and is whole-object-or-nothing: a leaf is either present and complete or absent, never half a body under a 200.
WHAT THIS FILE PROVIDES. One cxosub.Manager built over the dmsg client the reward server ALREADY holds — never a fresh identity; minting throwaway keys is the bug removed in #4501/#4502 — plus typed readers for the leaves the pages need:
stats/network replaces GET /all-transports/stats
stats/versions replaces GET /version
stats/daily replaces GET /metric?days=30
metrics/day/<date> replaces GET /metrics?days=N&bandwidth=true
AND the per-visor GET /metric/visor/{pks}
The last line is the largest win and the reason this file holds the per-transport feed at all. The per-transport day leaves carry `edges` and per-day, per-edge bandwidth, so per-visor bandwidth is a local sum by edge public key — the 1,400-character URL is not shortened, it is deleted. The same records are what the min()-verified network total is computed from, so that figure keeps its three-branch trust model (see fillTPDBandwidth) applied to identical inputs rather than being quietly swapped for TPD's own cumulative aggregate, which is a different number.
THE COST, stated plainly: the per-transport feed is ~30 day leaves of a few megabytes each. That is a large first fill. It is paid once per process — a settled day's leaf is content-addressed and hashes the same forever, so only the current day moves afterwards — and it replaces three separate HTTP bodies, one of which (the 7-day per-transport table) is tens of megabytes on every uncached request.
FALLBACK AND HONESTY. Every reader here returns a miss rather than an error the caller must interpret, and every caller falls back to its existing HTTP path on a miss. But a stale CXO snapshot and a fresh HTTP fetch are not the same number, so each reader also returns a statsSource the page prints. Naming the source is the same rule as naming an absence: the page must never present a figure without saying where it came from.
Package clirewardsserver cmd/skywire-cli/commands/rewards/server/survey_ingest.go c4-vis-cli
Visor survey PUSH ingest. A visor POSTs its node-info survey to the reward system over dmsg instead of the reward system pulling it hourly. The survey is stored under the visor's DMSG-AUTHENTICATED source PK (RemoteAddr), so a visor can only ever write its own survey. The response tells the visor whether it is reward-eligible so the visor can surface that in the hypervisor UI (a rejected push becomes a red mark in the node-list reward column, distinct from the hyphen shown when no reward address is set).
Storage target is log_backups/<pk>/node-info.json — the calc's authoritative source — so a pushed survey is available to the next calc run immediately, independent of the pull cycle's rsync (which matters because the pull is being reduced to a low-frequency fallback). The existing getlogs.sh age-out + the authoritative version prune over log_backups still apply.
Package clirewardsserver cmd/skywire-cli/commands/rewards/server/svgchart.go c4-vis-cli
Package clirewardsserver cmd/skywire-cli/commands/rewards/server/timeseries.go c4-vis-cli
Package clirewardsserver cmd/skywire-cli/commands/rewards/server/visorbandwidth.go c4-vis-cli
Index ¶
- Constants
- Variables
- func BuildHandler() http.Handler
- func ConfigureAndBuild(cfg RewardConfig) http.Handler
- func GenerateBandwidthHistoryChartHTML(history []BandwidthHistoryEntry, chartWidth, chartHeight int) string
- func GeneratePieChartHTML(items []PieChartItem, _ int) string
- func GenerateVersionHistoryChartHTML(history []VersionHistoryEntry, chartWidth, chartHeight int) string
- func GenerateVisorBandwidthChartHTML(history []BandwidthHistoryEntry, chartWidth, chartHeight, maxVisors int) string
- func RenderStatsANSI(d statsTUIData) string
- func RenderStatsHTMLFragment(d statsTUIData) string
- type BandwidthHistoryEntry
- type CountryStat
- type CountryStatsResponse
- type PieChartItem
- type RewardConfig
- type UptimeVisor
- type VersionHistoryEntry
Constants ¶
const ( // MononokiWoff2 is the WOFF2 format font data MononokiWoff2 = "" /* 38220-byte string literal not displayed */ // MononokiWoff is the WOFF format font data MononokiWoff = "" /* 60224-byte string literal not displayed */ )
Embedded mononoki font data as base64
Variables ¶
var LoginChainCmd = &cobra.Command{ Use: "loginchain", Short: "start the login chain nodes (block publisher + peer)", Long: `Starts a two-node login chain for wallet ownership verification. Block publisher on :6001 (P2P) / :6421 (API) Peer node on :6002 (P2P) / :6422 (API) — serves wallet API The reward system UI server connects to the peer node: skywire cli rewards ui --login-node http://127.0.0.1:6422 Blockchain data is wiped on every startup (fresh chain). The genesis wallet (login_genesis.json) is preserved across restarts. IMPORTANT: Run this command from the same directory as the reward server's -W working directory, or use -W to specify it explicitly. The loginchain reads login_genesis.json and login_fiber.toml from the working directory. A mismatch causes "no unspents to spend". LOGIN VERIFICATION FLOW 1. User submits their reward address (skycoin address or xpub) 2. Server funds a login address with coins on the login chain 3. User sends those coins back to the genesis address 4. Server confirms the transaction, proving wallet ownership For xpub users, the login address is derived from the change chain (m/44'/coin'/0'/1/0) to avoid collision with receiving addresses. This requires the account-level xpub (--path=0), not the external chain xpub shown in the Skycoin web wallet GUI (--path=0/0). See 'skywire reward --help' for xpub setup instructions.`, Run: func(_ *cobra.Command, _ []string) { runLoginChain() }, }
LoginChainCmd starts the login chain (block publisher + peer node). Run this as a separate process; the reward server connects via --login-node.
var ServerCmd = &cobra.Command{ Use: "ui", Short: "reward system UI server", Long: "skycoin reward system user interface server and skywire network metrics:\n https://theskywirenetwork.net\n" + calvin.AsciiFont("fiber") + func() string { if _, err := os.Stat(skyenvfile); err == nil { return `run the web application skyenv file detected: ` + skyenvfile } return `run the web application .conf file may also be specified with SKYENV=/path/to/fiber.conf fiber run` }(), Run: func(cmd *cobra.Command, _ []string) { r := buildRouter() serveStandalone(r, resolveBindAddr(cmd, webBind, webPort)) }, }
ServerCmd starts the reward system ui server
Functions ¶
func BuildHandler ¶
BuildHandler builds and returns the reward system's HTTP handler without starting any servers. The visor can mount this handler on its own port 80 gin router for integrated DMSG/skynet access.
Call SetConfig before BuildHandler to configure the reward system.
func ConfigureAndBuild ¶
func ConfigureAndBuild(cfg RewardConfig) http.Handler
ConfigureAndBuild sets the reward system's internal state from the given config and builds the HTTP handler. This is the entry point for visor integration — the visor calls this instead of running the CLI command.
func GenerateBandwidthHistoryChartHTML ¶
func GenerateBandwidthHistoryChartHTML(history []BandwidthHistoryEntry, chartWidth, chartHeight int) string
GenerateBandwidthHistoryChartHTML creates a CSS-based bar chart showing daily network bandwidth
func GeneratePieChartHTML ¶
func GeneratePieChartHTML(items []PieChartItem, _ int) string
GeneratePieChartHTML generates a CSS-based pie chart with legend GeneratePieChartHTML generates a pie chart. All items are shown — no grouping. The maxSlices parameter is ignored (kept for API compatibility).
func GenerateVersionHistoryChartHTML ¶
func GenerateVersionHistoryChartHTML(history []VersionHistoryEntry, chartWidth, chartHeight int) string
GenerateVersionHistoryChartHTML renders version adoption over time as an inline SVG stacked area.
This previously emitted one absolutely-positioned 1-pixel <div> per plotted pixel — 14,417 elements and 2.1 MB of HTML for two years of history, which took eight seconds to serve. The same data is now a handful of <path> elements. Hover detail is a <title> per band rather than per pixel.
func GenerateVisorBandwidthChartHTML ¶
func GenerateVisorBandwidthChartHTML(history []BandwidthHistoryEntry, chartWidth, chartHeight, maxVisors int) string
GenerateVisorBandwidthChartHTML creates a stacked bar chart showing per-visor bandwidth over time. Only the top maxVisors by total bandwidth are shown individually; the rest are grouped as "Other".
func RenderStatsANSI ¶
func RenderStatsANSI(d statsTUIData) string
RenderStatsANSI draws the whole page as ANSI. Exported so a TUI build can call it and print the result directly.
func RenderStatsHTMLFragment ¶
func RenderStatsHTMLFragment(d statsTUIData) string
RenderStatsHTMLFragment converts the ANSI rendering to an HTML fragment. The caller supplies the <pre>: fragment mode emits only the colored spans, so newlines collapse without one, and the page keeps ownership of its chrome.
Types ¶
type BandwidthHistoryEntry ¶
type BandwidthHistoryEntry struct {
Date string `json:"date"`
Total uint64 `json:"total"`
ByVisor map[string]uint64 `json:"by_visor,omitempty"`
VisorCount int `json:"visor_count"`
}
BandwidthHistoryEntry represents bandwidth totals for a single day
func ParseHistoricBandwidthData ¶
func ParseHistoricBandwidthData(histDir string) ([]BandwidthHistoryEntry, error)
ParseHistoricBandwidthData reads all *_bandwidth.json files from hist directory
type CountryStat ¶
type CountryStat struct {
Count int `json:"count"`
CountryName string `json:"country_name"`
CountryCode string `json:"country_code"`
Flag string `json:"flag"`
}
CountryStat represents visor count for a country
type CountryStatsResponse ¶
type CountryStatsResponse struct {
Stats []CountryStat `json:"stats"`
TotalNodes int `json:"total_nodes"`
Generated string `json:"generated"`
}
CountryStatsResponse is the JSON response for country statistics
type PieChartItem ¶
PieChartItem represents a single item in a pie chart
func ParseFrequencyStats ¶
func ParseFrequencyStats(statsText string) []PieChartItem
ParseFrequencyStats parses frequency statistics from text format (e.g., " 10 amd64\n 5 arm64")
type RewardConfig ¶
type RewardConfig struct {
WorkDir string
WhitelistPKs []cipher.PubKey
CanonicalDomain string
SkycoinNode string
LoginNode string
HealthOnly bool
DisableTpVizAPI bool // prevent tp-viz from mounting /api/* routes
}
RewardConfig holds configuration for the reward system when hosted by the visor. The visor constructs this from its config and calls ConfigureAndBuild to get the HTTP handler.
type UptimeVisor ¶
type UptimeVisor struct {
PK string `json:"pk"`
On bool `json:"on"`
Version string `json:"version"`
Daily map[string]string `json:"daily"`
}
UptimeVisor represents a visor entry from uptime tracker JSON
type VersionHistoryEntry ¶
type VersionHistoryEntry struct {
Date string `json:"date"`
Versions map[string]int `json:"versions"`
Total int `json:"total"`
}
VersionHistoryEntry represents version counts for a single day
func ParseHistoricUptimeData ¶
func ParseHistoricUptimeData(histDir string, minUptime float64) ([]VersionHistoryEntry, error)
ParseHistoricUptimeData reads all _ut.json files and returns version counts per day Only counts visors that had >= minUptime% uptime on each specific day
Source Files
¶
- cmd.go
- favicon.go
- fonts.go
- htmpl.go
- liveness.go
- logging.go
- login.go
- loginchain.go
- logo.go
- nodeproxy.go
- seo.go
- server.go
- stats.go
- stats_tui.go
- stats_tui_arch.go
- stats_tui_coverage.go
- stats_tui_data.go
- stats_tui_dmsg.go
- stats_tui_pervisor.go
- stats_tui_sn.go
- stats_tui_uptime.go
- statscxo.go
- survey_ingest.go
- svgchart.go
- timeseries.go
- visorbandwidth.go