Help us shape the future of Go! Take the 2026 Go Developer Survey

clirewardsserver

package
v1.3.97 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 27, 2026 License: AGPL-3.0 Imports: 60 Imported by: 0

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

View Source
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

View Source
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.

View Source
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

func BuildHandler() http.Handler

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

type PieChartItem struct {
	Label string
	Count int
}

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

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL