Skip to content

v0.6.0

Choose a tag to compare

@cmj0121 cmj0121 released this 07 May 02:00
· 42 commits to main since this release

Highlights

  • /github/:user and /github/:user/:repo — banner cards for any public GitHub user, organization, or repository. Profile cards render display name, bio, ★ followers · ⌥ repos · following · gists, company affiliation, blog homepage URL (scheme stripped), Twitter handle as x.com/<handle>, location · joined-since collapsed onto one row, and an Organization badge for org accounts. Repo cards use the repo NAME alone as headline (owner is implied by the URL — keeps banner width comparable to the user card), full description, ★ stars ⎇ forks ⚠ open-issues, language · license · default-branch, and pushed <relative> · release <tag> collapsed onto a single row. GITHUB_TOKEN env var is optional — when set, raises the per-process upstream cap from 60 to 5000 req/hr; token is env-only and redacted from any debug log. Per-IP rate limit (30/min/IP) on the route group; Vary: User-Agent on every response. Private repos return 404 even if the configured token has access — the route never exposes private repo data.

  • /dns/:hostname — banner card with all canonical DNS record types in a single lookup: A, AAAA, CNAME, MX, NS, TXT (prefix-classified into spf · dmarc · N verifications), SOA, CAA, SRV, plus a DNSSEC ✓ badge when the zone is signed. Hostname-as-banner with labeled tabular caption rows left-aligned via BannerBoxes, so labels and values align cleanly across rows. RFC 8552 underscored attribute labels supported (_dmarc.example.com, _acme-challenge.foo.com, _sip._tcp.example.com) — these are the route's headline use cases for email-auth and ACME debugging. DoT-by-default to 1.1.1.1:853,1.0.0.1:853 (cloudflare-dns.com SNI) so viewer queries don't leak in plaintext on the wire; DNS_RESOLVER accepts a comma-separated fallback list and DNS_RESOLVER_SNI overrides the TLS hostname for self-signed internal resolvers.

  • Hardened operational posture for the new routes — TTL-honoring cache (clamp(min upstream TTL, [60s, 600s]) for /dns, three-state for both routes covering success / transient-fail / NXDOMAIN). Stale-on-transient serves cached records with a ( stale data ) caption rather than a generic 502. Process-global rate gate (100 q/s sustain / 200 burst via golang.org/x/time/rate) on /dns/* caps egress to upstream even under botnet-driven 30/min/IP × N IPs fan-out. Self-throttle returns 503 + max-age=60 (semantically distinct from per-IP rate limit's 200 + max-age=60). robots.txt extended to Disallow: /github/ and Disallow: /dns/.

Other changes

  • Hostname validation on /dns/* enforces RFC 1035 LDH plus RFC 8552 leading underscore, max 253 chars, IDNA + lowercase + trim-trailing-dot canonicalization. Refused suffixes (.local, .localhost, .internal, .lan, .example, .test, .invalid, .arpa) return 400 before any wire query is issued. IP-literal-as-hostname rejection runs after canonicalize so trailing-dot variants don't evade.
  • Private-IP filter on /dns/* strips A/AAAA literals matching RFC1918 / loopback / link-local / unspecified / multicast. AAAA values are Unmap'd so IPv4-mapped IPv6 (::ffff:10.0.0.1) doesn't leak private addresses through the v6 form. CNAME, NS, MX, SRV, and TXT values pass through unchanged — the gap is documented honestly in docs/security.md.
  • Per-RR TXT classification on /dns/*: each dns.TXT.Txt is one RR's chunks; concatenate the chunks within a single RR, classify the joined-per-RR string against the prefix table (v=spf1, v=DMARC1, v=DKIM1, google-site-verification=, apple-domain-verification=, MS=, facebook-domain-verification=, stripe-verification=, _atproto), accumulate flags and counts across RRs.
  • Vary: User-Agent invariant tightened — /qr, /github/*, and /dns/* all emit it on every response (200/400/404/415/502/503). The latent CDN cache-poisoning bug at / and /stock (no Vary header on UA-class-variant or locale-variant bodies) remains pre-existing follow-up.
  • Forbidden-banner-glyph sanitizer on /github/* substitutes :;, %pct, , , ▲▼ . Leading _ on /dns/* headlines is substituted to a space because pylon parses leading underscore as a directive (same class of issue as leading @).
  • Per-route Cache-Control: /github/:user 200=600s, /github/:user/:repo 200=120s, /dns/* 200=300s, all routes 404=3600s, rate-limited=60s, 502/400=no-store.
  • New deps, all permissive: github.com/miekg/dns (BSD-3), github.com/foxcpp/go-mockdns (MIT, test only), golang.org/x/net/idna (BSD-3), golang.org/x/time/rate (Apache-2.0).

Container image

ghcr.io/cmj0121/imagelet:0.6.0