Victoriametrics
VictoriaMetrics query API for swamp — instant/range PromQL, scrape-target health, a node-exporter system overview, container memory rankings, and a metrics push.
Model:
@magistr/victoriametrics— queries a VictoriaMetrics (or Prometheus- compatible) HTTP endpoint via/api/v1/queryand/api/v1/query_range, and writes to it via/api/v1/import/prometheus. Methods:query(instant PromQL),query-range(range PromQL over a time window),health(scrape-target up/down from theupmetric),system-overview(CPU/memory/load/disk/network stats plus anomaly detection from node-exporter),container-memory(top-N container memory usage rankings), andpush(import Prometheus exposition text so a job can record its own outcome as a series vmalert can alert on).
2026.09.02.1
Restores the push method and its pushResult resource.
push existed on the 2026.08.08.1 model — it POSTs Prometheus exposition text
to /api/v1/import/prometheus so a job can record its own outcome as a series
vmalert can alert on. It was dropped when the model was rewritten for the
11-bug fix batch and never made it into git, so every published version since
has been missing it while one deployed copy of the old file kept using it. The
gap surfaced on 2026-09-02: swamp-fleet-update's publish-metrics step
succeeded on the server (still on the stale 2026.08.08.1 file) and failed
swamp workflow validate on the laptop with
Method 'push' not found on model type '@victoriametrics/query'. Syncing the
two copies in either direction would have silently killed the metrics.
push—lines(exposition text, blank lines and#comments allowed) and optionalextraLabels(comma-separatedkey=value) →pushResult.- Three pure helpers are now exported so they can be property-tested
directly:
sampleLines,metricNames,extraLabelParams. Before this release the model exported no parsing helpers at all. - A failed push writes
pushResultwithok:falsebefore throwing. A method that throws without writing leaves no record of what it tried to send, and the attempt that failed is the one worth keeping. - A transport failure records
httpStatus: 0rather than inventing a status, and a server error body is truncated to 300 characters so an unbounded response cannot become an unbounded resource. - The success body is read even on VM's empty
204, so the connection is not left dangling.
One real defect found while writing the tests
extraLabelParams trimmed each whole key=value pair, which silently rewrote
any value with leading or trailing whitespace — a= was sent as a=, so the
label VictoriaMetrics stored differed from the one the caller passed. Property
f2 (round-tripping arbitrary pairs back through URLSearchParams) shrank it
to {key:"a", value:" "}. Now only the key is trimmed, so
"job=x, instance=y" still parses while a value keeps every character it was
given; the split is at the first =, so an = inside a value survives, and a
pair whose key is empty is dropped rather than sent malformed.
Tests
15 new tests across four suites, all green (105 total):
- methods (5) — happy path pinning verb, endpoint,
text/plaincontent type and the exact body;extraLabels→ percent-encoded params; non-2xx and transport-failure paths asserting the resource is written and the error thrown; empty-payload rejection before any request is made. - adversarial (8) — HELP/TYPE and blank lines are not samples; a name ends
at the first
{or whitespace (a naivesplit(" ")[0]reportsa_metric{label="value); malformed pairs dropped; an injected&extra_label=cannot break out of the query string; a 200-with-error-body is still success (VM signals import failure with a non-2xx — characterization, not a bug); 300-char error truncation; the two whitespace/=regressions above. - coverage (4) — the
port || 8428fallback; no dangling?when there are no labels; exactly one trailing newline after trimming; the success-path body read (asserted viabodyUsed). - property (2) —
metricNamesreturns exactly the distinct emitted names in first-seen order andsampleLinescounts exactly the emitted samples, both oracled from the generator's own record rather than a re-parse; everyextraLabelspair round-trips as one param decoded byURLSearchParams.
No existing test changed. No existing resource schema changed; pushResult is
additive and the upgrades[] entry is an identity.
Global Arguments
| Argument | Type | Description |
|---|---|---|
| host | string | VictoriaMetrics host (IP or hostname) |
| port | number | VictoriaMetrics HTTP port |
Resources
2026.08.30.1
system-overview now counts each physical disk once.
node_exporter exports every layer of the block stack. On Unraid an encrypted
array slot is dm-N -> mdXp1 -> one physical sdX, so the disk view listed
the same spindle two or three times and a dm-*/sd* pair reporting the same
utilisation looked like two independent disks corroborating each other. On
Tower, 2026-08-30, dm-3 and sdl were flagged together as a disk-saturation
watch item: dm-3 = md4p1 = disk4 = /dev/sdl, one 14.6TB drive, and
neither layer was near saturation (7d mean 20%, peak 52%).
DISK_IO_QUERY(new export) adds{device!~"(dm-|md|loop|sr|zram|ram|nbd|drbd|zd).*"}so VictoriaMetrics drops the virtual layers at query time.isPhysicalDiskDevice()(new export) re-checks each returned series client-side, so a server that ignores the matcher still cannot double-count.- Both derive from one
VIRTUAL_DISK_PREFIXESlist, so the PromQL string and the client-side check cannot drift apart. Tests importDISK_IO_QUERYinstead of restating it. - Unlabelled series still normalise to
"unknown"and are kept — an unattributable series must not be silently dropped.
No resource schema change; identity upgrades[] entry appended.
2026.08.02.1
Real fixes for all 11 latent bugs tracked in the LOCAL
victoriametrics-latent-bugs issue-lifecycle model (NEVER filed to the
swamp.club Lab — see CLAUDE.md's anti-bypass rule). model.version and
manifest.yaml both bump 2026.07.16.2 -> 2026.08.02.1, with an identity
upgrades[] entry appended (no resource schema change).
- VM2 (HIGH, multi-series collapse):
extractValuesnow flatMaps EVERY series inresult.data.result(result.data.result.flatMap((s) => s.values ?? []).map(...)) instead of reading onlyresult.data.result[0].system-overview's CPU/memory/load stats now characterize the FULL set of series a range query returns, not just the first one. Single-series results are byte-identical (flatMap of one series equals that series). The flatten's inter-series timestamp boundary is a backward jump by construction and never trips the>600sforward-gap "Metric gap" reboot detector. - VM1 (MED,
healthhas no notion of expected targets):healthgains a new backward-compatible method argument,expectedTargets(default[]). Any name in that list not seen in theupvector is appended withstatus:"unknown"instead of being silently omitted. An empty/default list is a no-op — existing callers see no change. - VM3 (MED, absent metric indistinguishable from idle 0%):
system-overviewnow pushes a distinct"<CPU|Memory|Load> metric absent (no series returned)"anomaly whenever a metric's range query returns zero series. The numeric fields still report{0,0,0,0}(unchanged) — only the anomalies array gains the flag. - VM4 (MED, boot-time epoch fallback): an empty
node_boot_time_secondsresult no longer falls back tobootTs=0(the 1970 epoch, with an enormous computed uptime).uptime.bootTimeis now the string"unknown",uptime.uptimeMinutesis0, and a"Boot time unavailable"anomaly is pushed. A real boot time is completely unaffected. - VM5 (MED, scalar query garbage-mapping):
querynow dispatches ondata.resultType: a"scalar"result ([ts, "<value>"]) maps to a single[{metric:{}, value:<parsed number>}]row instead of the old two-row[{metric:undefined,value:null},{metric:undefined,value:null}]garbage. Vector results are unaffected (samer.value ? parseFloat(r.value[1]) : nullmapping as before); a matrix body fed intoquerystill maps every series tovalue:null(matrix support was never in scope forquery, and stays a documented, intentional gap). - VM6 + VM11 (MED, uninspected
{status:"error"}responses): a new sharedvmData(result)helper is called by the three direct single-query methods (query,query-range,health) right after the HTTP round-trip. It throwsError("VM query error: <result.error | 'response missing data'>")whenresult.status==="error"orresult.data.resultisn't an array, instead of letting a partial/hostile 200 body crash several frames down with an uncaughtTypeError.system-overviewandcontainer-memorydeliberately do NOT go throughvmData— they keep their existing lenient degrade-to-zero behavior on missing/empty data. - VM7 (MED,
query-rangevector-shaped-body crash):query-range's series mapper now does(r.values ?? []).map(...)— a series missingvalues(e.g. a vector body mistakenly fed toquery-range) degrades tovalues:[]instead of throwing. - VM8 (MED, unguarded inner
.values/.valueaccess): the disk loop,container-memory's series loop, andhealth's per-target status now all guard their inner access instead of crashing on a partial/hostile response: disk pre-filters series viaArray.isArray(r.values) && r.values.length>0before mapping;container-memoryuses(r.values ?? [])andr.metric?.name;healthusesr.value ? (...) : "unknown". - VM9 (MED, disk NaN/-Infinity): folds into the VM8 disk pre-filter — a
series with
values:[](present, empty) is now dropped BEFORE theMath.max/reducecomputation runs, instead of computingNaN/-Infinityand relying on the>10threshold filter to silently absorb it. The observable result (disk:[]) is unchanged. - VM10 (LOW, negative
topN):container-memory'stopNis now clamped viaMath.max(0, args.topN)before.slice(0, ...). A negativetopNnow yields[]instead ofslice(0, -n)silently dropping from the END of the already-desc-sorted array.topN:0is unaffected (Math.max(0,0)===0). - Tests: all 11 latent-bug pins in
victoriametrics_adversarial_test.tsflip from characterizing the bug (title prefixedpin:) to asserting the fix (title prefixedfix (VMn):), reusing the existing fixtures plus a handful of new inline hostile bodies. New adversarial coverage: two multi-series regressions for VM2 (a LOAD spike
Release 2026.07.16.2 — align model versions with manifests
Maintenance release across the @magistr extensions. For most packages this
carries no functional change: the only edit is the model's version: field,
brought back in line with its manifest version so the published model type
version and the package version no longer drift.
Functional changes in this release are limited to:
anime-cron: normalizeTitle now strips a ": subtitle" suffix and a trailing parenthesized year before comparison, fixing dedup false-misses where the torrent title carries a subtitle or year that the AniList romaji does not.
arckit: first publish. Standalone ArcKit port — a 12-phase architecture governance state machine with 65 bundled templates, driven by a bundled skill.
Also tracks three extensions (kaiten, observability-agent, music-library) that previously existed only as untracked working-tree directories, recovered from stashes.
Maintenance version bump. No functional changes.
Added 1 models
Merge pull request #5 from umag/extensions/jscad-stl-pair
extensions: add 15 more @magistr extensions + auto-discover CI
- Has README or module doc2/2earned
- README has a code example1/1earned
- README is substantive1/1earned
- Most symbols documented1/1earned
- No slow types (deprecated)1/1earned
- Dependencies pass trust audit2/2earned
- Has description1/1earned
- Platform support declared (or universal)2/2earned
- License declared1/1earned
- Verified public repository2/2earned