Gonic
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 underlyinggonic.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.
Global Arguments
| Argument | Type | Description |
|---|---|---|
| host | string | Gonic host (IP or hostname) |
| port | number | Gonic HTTP port |
| username | string | Subsonic API username |
| password | string | Subsonic API password |
| sshUser | string | SSH user for DB access |
| dbPath | string | Path to gonic.db on the host |
Resources
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-querynow REALLY enforces read-only, FIXED.sshExecSqlgained areadOnlyparameter;db-querypassesreadOnly: true, so the command line issqlite3 -json -readonly '<dbPath>'(precedent: music-library'ssqlite3 -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-execandensure-podcast-dirsare unaffected (readOnlydefaults tofalse). - LB4 —
db-exec's change-count is now connection-scoped, FIXED. The write andSELECT changes()now run as ONE combined statement (<sql>;\nSELECT changes();) on a SINGLEsshExecSqlsession, 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) makessshExecSqlthrow before any count is reported — no bogus count on failure. - LB5 — network-error credential redaction, FIXED.
gonicApinow wrapsawait fetch(url)in a try/catch (precedent: telegram-send'sredactToken/redactedCause). A rejection is rethrown viaredactSecret, which redacts the raw password AND its hex encoding (plus a genericenc:<hex>backstop) from the WHOLE error — message,cause, andcause's.stack— not just.message. The!resp.okand failed-envelope throws stay OUTSIDE the try/catch, so the "full body verbatim" pins are unchanged. - LB7 —
dbResultname 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 frozenDatein tests) get DISTINCT resource names instead of clobbering. - LB8 —
sshExecSqlno longer swallows warning-only stderr failures, FIXED. When!output.success, it now always throws — using the warning-filteredrealErrorswhen non-empty, else falling back to rawstderr/stdout/"unknown error"— so asuccess: falseresult whose stderr contains ONLY an SSH "Warning: Permanently added..." line is surfaced instead of silently treated as success. The real-error case (realErrorsnon-empty) is byte-identical to before. - LB9 — README doc drift, FIXED. The instance example's
typeVersionnow reads2026.08.02.1instead of the stale2026.05.25.1. - Model
versionandmanifest.yamlbumped to2026.08.02.1(kept in sync); added an identity no-opupgrades[]entry (toVersion: "2026.08.02.1") — none of LB3-LB9 changeglobalArgumentsor 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-readonlyon 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-readonlytoken (and its slice prefix), with the POSIX-grammar assertion kept intact; (h) thedbResultname-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-readonlyon 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/007parseIntfallback 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
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-dirsnow quotes the DB-sourcedroot_dir/host-mount path with ashellEschelper (copied verbatim fromfirecracker/extensions/models/firecracker.ts: wrap in single quotes, escape each embedded'as'\'') before interpolating it intomkdir -p <hostDir>. Applied tosshExecSql'sdbPathinterpolation in the same change (smaller blast radius — operator-controlled config, not live DB content — but trivially shared hardening).mkdirruns on the REMOTE gonic host over SSH, soDeno.mkdiris not a substitute for this fix. - HIGH — URL-query credential leak, FIXED.
buildAuthParamsno longer sendsp=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 TOKENt = md5hex(password + salt)viajsr:@std/crypto(MD5 is absent from Deno's nativeWebCrypto subtle.digest), and emitsu, t, s, v, c, f—pis dropped entirely. gonic's server fully supports this scheme (ctrlsubsonic/ctrl.go'scheckCredsToken); see the corrected auth-reality note in Unreleased above.buildAuthParamsis nowasync;gonicApiawaits it. - Added
jsr:@std/crypto@1todeno.jsonimports; regenerateddeno.lockunder deno 2.8.3. - Model
versionandmanifest.yamlbumped to2026.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 ingonic_adversarial_test.tsnow assert the FIX (nopparam / password not recoverable; the injected'is neutralized rather than breaking out); theu/passertion ingonic_methods_test.tsnow assertst/s(theu/v/c/fassertions are unchanged); the three enc-hex relation/recovery/collapse properties ingonic_property_test.tscollapse into one token-auth invariant (pabsent,shex-shaped,trecomputed asmd5hex(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,dbResultname clobber,sshExecSqlwarning-swallow, fixtures-secret-scan, and every quote-free happy-pathmkdir/sqlite3command-line assertion) is unchanged and stays green —shellEscon 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, thedbResultname clobber,sshExecSql's warning-swallow, and the stale READMEtypeVersion. Token auth overhttp://still permits passive replay of a capturedt+spair for that one salt — it does not expose the password; forcinghttps://is out of scope.
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.
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