<feed xmlns='http://www.w3.org/2005/Atom'>
<title>skunky-art.git/app, branch v1.3.3</title>
<subtitle>alternative deviantart frontend, no javascript. go.
</subtitle>
<id>http://git.krz.sh/krz/skunky-art.git/atom?h=v1.3.3</id>
<link rel='self' href='http://git.krz.sh/krz/skunky-art.git/atom?h=v1.3.3'/>
<link rel='alternate' type='text/html' href='http://git.krz.sh/krz/skunky-art.git/'/>
<updated>2026-07-15T07:25:20+00:00</updated>
<entry>
<title>fix: document exported API, fix naming, and harden Download</title>
<updated>2026-07-15T07:25:20+00:00</updated>
<author>
<name>Christian Cleberg</name>
<email>hello@cleberg.net</email>
</author>
<published>2026-07-15T07:25:20+00:00</published>
<link rel='alternate' type='text/html' href='http://git.krz.sh/krz/skunky-art.git/commit/?id=d6789522d3dd076f7c98dafa3e7d42df09252093'/>
<id>urn:sha1:d6789522d3dd076f7c98dafa3e7d42df09252093</id>
<content type='text'>
The remaining golangci-lint findings. These land together because the
Url -&gt; URL rename spans util.go, parsers.go and wrapper.go, and splitting
it would leave an intermediate commit that does not compile.

Download() carried the most serious bug here: try() only prints an error,
it does not return, so a failed request fell through to
resp.Body.Close() on a nil resp and panicked. Every failure path now
returns the zero Downloaded, and callers check Status. ReturnHTTPError
guards against the resulting status 0, which would otherwise panic
WriteHeader. Requests carry a context with a timeout (noctx), and a
download-proxy now routes through ProxiedTransport so it keeps the DA
throttle and timeouts.

Also:
- doc comments on all 48 exported symbols (revive's exported rule, with
  checkPrivateReceivers, since most of app is exported methods on the
  unexported skunkyart type), plus package docs in new doc.go files so
  both the embed and non-embed builds are covered.
- ST1003 naming: UrlBuilder -&gt; URLBuilder, id_search -&gt; idSearch,
  cache_config -&gt; cacheConfig, TXT_RAW -&gt; TxtRaw, mediaUrl -&gt; mediaURL.
- explicit json tags on structs that are unmarshaled (musttag); the
  hyphenated keys already had tags, the rest relied on case-insensitive
  fallback. Behaviour is unchanged.
- modernization: range-over-int, WaitGroup.Go, stale +build lines,
  interface{} -&gt; any, strings.Builder over string concatenation in a loop.
</content>
</entry>
<entry>
<title>fix: harden HTTP transport, server timeouts and panic paths</title>
<updated>2026-07-15T07:24:58+00:00</updated>
<author>
<name>Christian Cleberg</name>
<email>hello@cleberg.net</email>
</author>
<published>2026-07-15T07:24:58+00:00</published>
<link rel='alternate' type='text/html' href='http://git.krz.sh/krz/skunky-art.git/commit/?id=7eb5e5e2230b6fb5b1bed6f0eaa03279aa61555b'/>
<id>urn:sha1:7eb5e5e2230b6fb5b1bed6f0eaa03279aa61555b</id>
<content type='text'>
Correctness and security findings surfaced by golangci-lint, plus two
latent panics found alongside them.

- router: http.ListenAndServe has no timeouts at all (gosec G114), so a
  slow client could hold a connection and its handler open indefinitely.
  Replace it with an explicit http.Server carrying read/write/idle
  timeouts.
- httpclient: InstallDAThrottle asserted http.DefaultTransport was a
  *http.Transport and would panic outright if anything had already
  wrapped it -- which is precisely what that function does. Check the
  assertion and fall back to a fresh transport. Expose ProxiedTransport
  so a configured download-proxy can inherit the same throttle and
  timeouts instead of silently bypassing them.
- cache: the Sys() assertion to *syscall.Stat_t is only valid on unix and
  would panic elsewhere; skip rotation instead. Indexing
  Headers["Content-Type"][0] panics when the header is absent; use
  Headers.Get. Cache files are written 0600 rather than 0700, as they are
  never executed.
- cli, api: check error returns, and exit rather than nil-dereference a
  file handle that failed to open.

SHA-1 and math/rand keep //nolint:gosec with reasons: they are cache-key
hashes and random-artwork picks, not security primitives.
</content>
</entry>
<entry>
<title>fix(cache): correct max-size megabyte conversion</title>
<updated>2026-07-15T07:23:59+00:00</updated>
<author>
<name>Christian Cleberg</name>
<email>hello@cleberg.net</email>
</author>
<published>2026-07-15T07:23:59+00:00</published>
<link rel='alternate' type='text/html' href='http://git.krz.sh/krz/skunky-art.git/commit/?id=40d405f318a109aad5e7de7ad71fedcc04bb0e86'/>
<id>urn:sha1:40d405f318a109aad5e7de7ad71fedcc04bb0e86</id>
<content type='text'>
max-size is documented in SETUP.md as megabytes, but the conversion was
`*= 1024 ^ 2`. In Go `^` is XOR, not exponentiation, so this multiplied
by 1026 rather than 1048576 -- a configured max-size of 200 produced a
~205 KB cap instead of 200 MB, wiping the cache almost immediately.

This changes runtime behaviour: the cache will now grow to the size the
config actually asks for.
</content>
</entry>
<entry>
<title>fix: remove russian language throughout</title>
<updated>2026-07-15T06:58:41+00:00</updated>
<author>
<name>Christian Cleberg</name>
<email>hello@cleberg.net</email>
</author>
<published>2026-07-15T06:54:26+00:00</published>
<link rel='alternate' type='text/html' href='http://git.krz.sh/krz/skunky-art.git/commit/?id=b1ad14553277d9d72a5e4b4462a52a2e2d0fdbb1'/>
<id>urn:sha1:b1ad14553277d9d72a5e4b4462a52a2e2d0fdbb1</id>
<content type='text'>
</content>
</entry>
<entry>
<title>fix: point source links and instance fetch at this fork</title>
<updated>2026-07-15T01:28:02+00:00</updated>
<author>
<name>Christian Cleberg</name>
<email>hello@cleberg.net</email>
</author>
<published>2026-07-15T01:28:02+00:00</published>
<link rel='alternate' type='text/html' href='http://git.krz.sh/krz/skunky-art.git/commit/?id=0aff13949a8e9e120a585f00627ef6959f8e0635'/>
<id>urn:sha1:0aff13949a8e9e120a585f00627ef6959f8e0635</id>
<content type='text'>
Upstream references still pointed at git.macaw.me, which is unmaintained
and whose instances.json is a dead link. Repoint them at this repo:

- RefreshInstances now fetches instances.json from raw.githubusercontent
- source/version links in index.htm, about.htm, and CLI help use the
  GitHub releases tag URL
- SETUP.md/SETUP-RU.md language switch links were Gitea-absolute paths
  that 404 on GitHub; make them relative
- --add-instance message says 'main' branch, matching this repo

The fork attribution in README.md is left as-is: it credits upstream
rather than pointing at the source.
</content>
</entry>
<entry>
<title>feat: throttle and time out outbound DeviantArt requests</title>
<updated>2026-07-15T01:22:26+00:00</updated>
<author>
<name>Christian Cleberg</name>
<email>hello@cleberg.net</email>
</author>
<published>2026-07-15T01:22:26+00:00</published>
<link rel='alternate' type='text/html' href='http://git.krz.sh/krz/skunky-art.git/commit/?id=5763dc3cfeadf491d6eda3b6a91623f86396a134'/>
<id>urn:sha1:5763dc3cfeadf491d6eda3b6a91623f86396a134</id>
<content type='text'>
DeviantArt fronts its API with CloudFront + WAF, which bans egress IPs
that hit it too hard. devianter issues requests with a bare http.Client,
so unbounded concurrent handlers each pulled ~150-200 KB of JSON, which
both risked a ban and could exhaust the process under a bot flood.

Wrap the default transport to bound rate and concurrency for
deviantart.com and add timeouts. Other hosts (wixmp image CDN) pass
straight through, so media stays fast, and ProxyFromEnvironment is
preserved so HTTPS_PROXY egress still works.
</content>
</entry>
<entry>
<title>fix(api): harden random-art retry loop</title>
<updated>2026-07-15T01:22:14+00:00</updated>
<author>
<name>Christian Cleberg</name>
<email>hello@cleberg.net</email>
</author>
<published>2026-07-15T01:22:14+00:00</published>
<link rel='alternate' type='text/html' href='http://git.krz.sh/krz/skunky-art.git/commit/?id=8a00f7c55cdbcddcd878c01a79f2db7dc3f080ef'/>
<id>urn:sha1:8a00f7c55cdbcddcd878c01a79f2db7dc3f080ef</id>
<content type='text'>
Three bugs in Random():

- The retry loop was unbounded. Only the NSFW path incremented attempt,
  so a run of DeviantArt errors span forever, hammering the API and
  risking an egress-IP ban.
- string(rand.Intn(999)) converts a rune, not a number: string(65) is
  "A", not "65". Searches were querying garbage. Use strconv.Itoa.
- rand.Intn panics on 0, so an empty result set crashed the handler.
  Skip empty results.

The exhausted-retries error now fires when the loop ends rather than
falling through to index an empty slice.
</content>
</entry>
<entry>
<title>deps: migrate to github.com/zerolabsco/devianter v0.3.1</title>
<updated>2026-07-15T01:22:04+00:00</updated>
<author>
<name>Christian Cleberg</name>
<email>hello@cleberg.net</email>
</author>
<published>2026-07-15T01:22:04+00:00</published>
<link rel='alternate' type='text/html' href='http://git.krz.sh/krz/skunky-art.git/commit/?id=0fdc45a668d95d603a9013f90392f6bc966c72f9'/>
<id>urn:sha1:0fdc45a668d95d603a9013f90392f6bc966c72f9</id>
<content type='text'>
Repoint the import path from git.macaw.me/skunky/devianter, replacing a
placeholder v0.0.0 require that had no go.sum entry and did not resolve.

v0.3.1 reorders the last two return values of PerformSearch, Group.Get
and Group.Gallery from (..., error, Error) to (..., Error, error);
adapt the call sites. The two types differ, so the compiler enforces
this.
</content>
</entry>
<entry>
<title>build: fix build on darwin</title>
<updated>2026-07-15T01:21:21+00:00</updated>
<author>
<name>Christian Cleberg</name>
<email>hello@cleberg.net</email>
</author>
<published>2026-07-15T01:21:21+00:00</published>
<link rel='alternate' type='text/html' href='http://git.krz.sh/krz/skunky-art.git/commit/?id=6b8e23001283acb95d10c275155e9cb83b15f598'/>
<id>urn:sha1:6b8e23001283acb95d10c275155e9cb83b15f598</id>
<content type='text'>
macOS names the stat ctime field Ctimespec rather than Ctim, so stat.go
failed to compile on a Mac. Split the darwin case into its own file and
exclude darwin from the generic variant. Deploys are unaffected.
</content>
</entry>
<entry>
<title>Add da.opnxng.com + lost-skunk.cc is now in Finland</title>
<updated>2025-04-07T10:53:47+00:00</updated>
<author>
<name>lost+skunk</name>
<email>me@lost-skunk.cc</email>
</author>
<published>2025-04-07T10:53:47+00:00</published>
<link rel='alternate' type='text/html' href='http://git.krz.sh/krz/skunky-art.git/commit/?id=210c69e48c2af4d1fdb53c0654ed33a21688696c'/>
<id>urn:sha1:210c69e48c2af4d1fdb53c0654ed33a21688696c</id>
<content type='text'>
</content>
</entry>
</feed>
