Skip to main content

Gonic

@magistr/gonicv2026.10.25.10· MIT· 1d agoBEHAVIORS
01README

Gonic Subsonic-compatible music server API for swamp — browse, search, stream, and scrobble against the Subsonic REST API, manage podcasts, and drive library scans.

Model:

  • @magistr/gonic — connectivity ping plus podcast management (list, refresh, delete channel/episode, trigger download), library scan status and rescan, and playlist listing over the Subsonic REST API. Also offers direct SQLite maintenance helpers over SSH (read-only query, write exec, and ensure-podcast-dirs) for the underlying gonic.db.

The Subsonic password is marked sensitive so it is never persisted in cleartext; database access uses non-interactive SSH (BatchMode) so it never blocks on a prompt.

02Release Notes

2026.10.25.10

  • Repo-wide republish; no functional change.
03Behaviors1
@magistr/gonicv2026.10.25.10extensions/models/gonic.ts

Global Arguments

ArgumentTypeDescription
hoststringGonic host (IP or hostname)
port?numberGonic HTTP port
usernamestringSubsonic API username
passwordstringSubsonic API password
sshUser?stringSSH user for DB access
dbPath?stringPath to gonic.db on the host
fn ping()
Test connectivity to the Gonic server
fn get-podcasts()
List all podcast channels with episodes
fn refresh-podcasts()
Refresh all podcast feeds (admin only)
fn delete-podcast-channel(id: string)
Delete a podcast channel by ID (admin only)
ArgumentTypeDescription
idstringPodcast channel ID (e.g. pd-5)
fn delete-podcast-episode(id: string)
Delete a podcast episode by ID (admin only)
ArgumentTypeDescription
idstringPodcast episode ID (e.g. pe-42)
fn download-podcast-episode(id: string)
Trigger download of a podcast episode (admin only)
ArgumentTypeDescription
idstringPodcast episode ID (e.g. pe-42)
fn scan-status()
Get current library scan status
fn start-scan()
Trigger a library rescan
fn get-playlists()
List all playlists
fn ensure-podcast-dirs()
Create missing podcast directories on the host for all channels
fn db-query(sql: string)
Run a read-only SQL query on the gonic database
ArgumentTypeDescription
sqlstringSQL SELECT query to run
fn db-exec(sql: string)
Run a write SQL statement on the gonic database (DELETE, UPDATE, INSERT)
ArgumentTypeDescription
sqlstringSQL statement to execute

Resources

podcasts(infinite)— Podcast channels with episodes
scanStatus(infinite)— Library scan status
playlists(infinite)— Playlist summaries
serverStatus(infinite)— Server status info
dbResult(infinite)— Database query result
04Previous Versions16
2026.10.15.9MIT

2026.10.15.9

Changed

  • Repo-wide maintenance release: version bump to republish the current source. No schema change.
2026.10.15.8MIT

2026.10.15.8

Changed

  • Repo-wide maintenance release: version bump to republish the current source. No schema change.
2026.10.15.6MIT

2026.10.15.6

Changed

  • Repo-wide maintenance release: version bump to republish the current source. No schema change.
2026.10.15.5MIT

2026.10.15.5

Changed

  • Repo-wide maintenance release: version bump to republish the current source. No schema change.
2026.10.15.4MIT

2026.10.15.4

Changed

  • Repo-wide maintenance release: version bump to republish the current source. No schema change.
2026.10.15.3MIT

2026.10.15.3

Changed

  • Repo-wide maintenance release: version bump to republish the current source. No schema change.
2026.10.15.2MIT

2026.10.15.2

Changed

  • Repo-wide maintenance release: version bump to republish the current source. No schema change.
2026.10.15.1MIT

2026.10.15.1

Changed

  • Repo-wide maintenance release: version bump to republish the current source. No schema change.
2026.10.14.2MIT(was none)

2026.10.14.2

Changed

  • Repo-wide maintenance release: version bump to republish the current source. No schema change.

Modified 1 behaviors

2026.09.19.2

2026.09.19.2

Changed

  • Repo-wide maintenance release: version bump to republish the current source. No schema change.
2026.09.17.1

2026.09.17.1

Changed

  • Repo-wide maintenance release: version bump to republish the current source. No schema change.
2026.08.19.1
2026.08.02.1

2026.08.02.1

Real-fixes the six remaining open findings tracked by the local gonic-latent-bugs issue-lifecycle model (LB3, LB4, LB5, LB7, LB8, LB9) — every one of these was previously characterized/PINNED as buggy behavior; this release closes each of them and flips its pin to "fixed". LB1/LB2/LB6 (fixed in 2026.08.01.1) are untouched and stay green.

  • LB3 — db-query now REALLY enforces read-only, FIXED. sshExecSql gained a readOnly parameter; db-query passes readOnly: true, so the command line is sqlite3 -json -readonly '<dbPath>' (precedent: music-library's sqlite3 -json -readonly). A mutating statement is rejected by sqlite3's own read-only connection (non-zero exit, "attempt to write a readonly database") — stronger than SQL-string sniffing. db-exec and ensure-podcast-dirs are unaffected (readOnly defaults to false).
  • LB4 — db-exec's change-count is now connection-scoped, FIXED. The write and SELECT changes() now run as ONE combined statement (<sql>;\nSELECT changes();) on a SINGLE sshExecSql session, instead of two separate sqlite3 connections. The reported count is parsed from that session's own trailing output line, so it is structurally derived from the write's real effect. On a failing write, sqlite3's non-zero exit (LB8) makes sshExecSql throw before any count is reported — no bogus count on failure.
  • LB5 — network-error credential redaction, FIXED. gonicApi now wraps await fetch(url) in a try/catch (precedent: telegram-send's redactToken/redactedCause). A rejection is rethrown via redactSecret, which redacts the raw password AND its hex encoding (plus a generic enc:<hex> backstop) from the WHOLE error — message, cause, and cause's .stack — not just .message. The !resp.ok and failed-envelope throws stay OUTSIDE the try/catch, so the "full body verbatim" pins are unchanged.
  • LB7 — dbResult name clobber, FIXED. A module-level monotonic counter (dbResultTag()) is appended to the full ms+Z timestamp for all three name builders (dirs-, query-, exec-), so two calls within the identical wall-clock instant (even under a frozen Date in tests) get DISTINCT resource names instead of clobbering.
  • LB8 — sshExecSql no longer swallows warning-only stderr failures, FIXED. When !output.success, it now always throws — using the warning-filtered realErrors when non-empty, else falling back to raw stderr/stdout/"unknown error" — so a success: false result whose stderr contains ONLY an SSH "Warning: Permanently added..." line is surfaced instead of silently treated as success. The real-error case (realErrors non-empty) is byte-identical to before.
  • LB9 — README doc drift, FIXED. The instance example's typeVersion now reads 2026.08.02.1 instead of the stale 2026.05.25.1.
  • Model version and manifest.yaml bumped to 2026.08.02.1 (kept in sync); added an identity no-op upgrades[] entry (toVersion: "2026.08.02.1") — none of LB3-LB9 change globalArguments or any resource schema.
  • Test flips (characterization → fix-verification), all in extensions/models/:
    • gonic_adversarial_test.ts: (b) the HONEST-GAP fetch-rejection pin now asserts REDACTION (no sentinel password/hex in message, cause, or stack; <redacted> marker present); (e) the db-query "no read-only guard" pin now asserts -readonly on the command line AND a real, non-vacuous sqlite3 rejection of a DELETE; (f) the db-exec change-count pin now asserts a SINGLE combined invocation whose reported count derives from its own connection; (d2) the LB6 dbPath-escaping pin's expected command line absorbs the new -readonly token (and its slice prefix), with the POSIX-grammar assertion kept intact; (h) the dbResult name-clobber pin now asserts DISTINCT names under a frozen Date, matching the new ms+Z+counter-suffixed regex. (g) hostile-Subsonic-response pins are unrelated and stay unchanged.
    • gonic_coverage_test.ts: the db-query/db-exec command-line guard now expects -readonly on db-query and a single db-exec invocation (dropped the third command-line assertion); the warning-only-stderr guard flips from "SWALLOWS" to "SURFACES" (now asserts a throw); the real-error guard is unchanged; the NaN/007 parseInt fallback guards are rerouted to the single combined invocation (same assertions, one stub call instead of two).
    • gonic_methods_test.ts: db-query's happy-path command-line assertion gains -readonly; db-exec's happy path and error path are rewritten from "two

Notes trimmed at a line boundary to fit the registry's 4900-byte per-version cap. Full section: https://github.com/umag/swamp-workspace/blob/d850d8e8372d4c0008c9245959a090b37095de7a/gonic/CHANGELOG.md

2026.08.01.1

2026.08.01.1

Fixes the two HIGH-severity findings tracked by the local gonic-latent-bugs issue-lifecycle model (never filed against the Lab — see that model for full detail and the deferred MED/LOW findings, which are unchanged and stay pinned).

  • HIGH — command injection, FIXED. ensure-podcast-dirs now quotes the DB-sourced root_dir/host-mount path with a shellEsc helper (copied verbatim from firecracker/extensions/models/firecracker.ts: wrap in single quotes, escape each embedded ' as '\'') before interpolating it into mkdir -p <hostDir>. Applied to sshExecSql's dbPath interpolation in the same change (smaller blast radius — operator-controlled config, not live DB content — but trivially shared hardening). mkdir runs on the REMOTE gonic host over SSH, so Deno.mkdir is not a substitute for this fix.
  • HIGH — URL-query credential leak, FIXED. buildAuthParams no longer sends p=enc:<hex(password)> (a trivially-reversible encoding) as a URL query parameter. It now generates a random per-request salt (crypto.getRandomValues), computes a Subsonic TOKEN t = md5hex(password + salt) via jsr:@std/crypto (MD5 is absent from Deno's native WebCrypto subtle.digest), and emits u, t, s, v, c, f — p is dropped entirely. gonic's server fully supports this scheme (ctrlsubsonic/ctrl.go's checkCredsToken); see the corrected auth-reality note in Unreleased above. buildAuthParams is now async; gonicApi awaits it.
  • Added jsr:@std/crypto@1 to deno.json imports; regenerated deno.lock under deno 2.8.3.
  • Model version and manifest.yaml bumped to 2026.08.01.1 (kept in sync).
  • Test flips (characterization → fix-verification), all in extensions/models/: the enc-hex recovery pin and the mkdir-injection pin in gonic_adversarial_test.ts now assert the FIX (no p param / password not recoverable; the injected ' is neutralized rather than breaking out); the u/p assertion in gonic_methods_test.ts now asserts t/s (the u/v/c/f assertions are unchanged); the three enc-hex relation/recovery/collapse properties in gonic_property_test.ts collapse into one token-auth invariant (p absent, s hex-shaped, t recomputed as md5hex(password + the emitted per-request salt) — necessarily derived from the captured salt, since the salt is random). Every other pinned characterization test (db-query not-read-only, db-exec change-count, fetch-rejection honest gap, domain-error non-leak, dbResult name clobber, sshExecSql warning-swallow, fixtures-secret-scan, and every quote-free happy-path mkdir/sqlite3 command-line assertion) is unchanged and stays green — shellEsc on a quote-free input is byte-identical to the old bare single-quoting.
  • Out of scope, deferred as before: db-query's missing read-only guard, db-exec's structurally-disconnected change-count, the fetch-rejection honest gap, the dbResult name clobber, sshExecSql's warning-swallow, and the stale README typeVersion. Token auth over http:// still permits passive replay of a captured t+s pair for that one salt — it does not expose the password; forcing https:// is out of scope.
2026.07.16.2

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.

2026.05.25.1

Merge pull request #5 from umag/extensions/jscad-stl-pair

extensions: add 15 more @magistr extensions + auto-discover CI

05Stats
A
100 / 100
Downloads
40
Archive size
81.3 KB
  • 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
  • Docs keep secrets in a vault1/1earned
  • Has description1/1earned
  • Platform support declared (or universal)2/2earned
  • License2/2earned
  • Verified public repository2/2earned
06Platforms
07Labels