gh-image reads browser cookies through browserutils/kooky. That dependency is now the single largest source of open issues in this repo, and several of them can't be closed here at all — they close when someone else merges and releases.
This issue is to decide whether that stays true, not to do the work.
The problem
Everything under label:upstream traces to kooky or its own dependencies:
Two open drafts (#27, #40) currently carry replace directives onto forks. That's a workable stopgap but it means go.mod points at unreleased third-party code, which is not something to ship in a release.
Upstream status
Worth being precise, because every option below depends on it. kooky is not dead, but not actively maintained either:
- The README carries a
No Maintenance Intended badge — and, right beside it, a PRs Welcome badge. Those have coexisted for a while.
- Development is bursty: a cluster of commits in March 2026, another in June, quiet since. Last commit
2026-06-15. Releases follow the same pattern (v0.2.9 in March, v0.2.10 in June).
- One person,
srlehn, is the only committer across the entire recent history. The repo is not archived and has ~289 stars.
- The PR backlog is the real signal.
#111 (Add Arc browser support) has been open since 2026-03-26 — four months. #110 (Dia) likewise.
Current coupling is small
Worth stating plainly, because it changes the cost of every option. The entire kooky surface is in internal/cookies/cookies.go:
kooky.ReadCookies(ctx, kooky.Valid, kooky.DomainHasSuffix("github.com")) — one call
*kooky.Cookie → local rawCookie in mapKookyCookies, already deliberately decoupled so selection logic is testable without kooky
- Seven blank imports registering browser finders
- Substring matching on kooky's unexported error strings in
browserReadHints — the fragile part, already flagged in a comment as needing re-verification on every bump
So the abstraction boundary mostly exists already. What kooky actually provides is not API surface, it's per-platform decryption and store discovery: DPAPI/ABE on Windows, Keychain on macOS, libsecret/kwallet on Linux, plus Chromium and Firefox store formats. That is the part that's expensive to own.
Options
Options 1–3 all reduce dependence on kooky. Option 4 goes the other way — it keeps the dependency and removes the bottleneck instead.
0. Status quo. Keep consuming releases, send fixes upstream. Zero cost, no control over timing. Given the four-month backlog above, "no control over timing" is doing a lot of work in that sentence.
1. Vendor a subset into internal/. Copy only the Chromium + Firefox read paths for the platforms we support. Full control, no release chain. Cost: we inherit ABE, Keychain, and keyring maintenance forever, including future Chrome encryption changes.
2. Maintained fork (drogers0/kooky) + replace. Cheap to start, lets #27 and #40 ship now. Cost: divergence and a permanent merge burden; replace directives in a released module are a smell.
3. Switch libraries. Needs a survey of what else exists in Go and whether any handles ABE v20.
4. Become a kooky maintainer. Ask srlehn / the browserutils org for commit and release rights — either for me personally or for someone else willing to take it on. Rather than routing around the bottleneck, remove it. Release rights specifically matter: commit access without the ability to tag is only half the bottleneck.
Not urgent. GH_SESSION_TOKEN is a complete escape hatch for every affected user today.
gh-imagereads browser cookies throughbrowserutils/kooky. That dependency is now the single largest source of open issues in this repo, and several of them can't be closed here at all — they close when someone else merges and releases.This issue is to decide whether that stays true, not to do the work.
The problem
Everything under
label:upstreamtraces to kooky or its own dependencies:v20) on Windows can't be decryptedbrowserutils/sqlite3#1, then needs a kooky bump, then a bump here — a three-hop release chainbrowserutils/kooky#114browserutils/kooky#116Two open drafts (#27, #40) currently carry
replacedirectives onto forks. That's a workable stopgap but it meansgo.modpoints at unreleased third-party code, which is not something to ship in a release.Upstream status
Worth being precise, because every option below depends on it. kooky is not dead, but not actively maintained either:
No Maintenance Intendedbadge — and, right beside it, aPRs Welcomebadge. Those have coexisted for a while.2026-06-15. Releases follow the same pattern (v0.2.9in March,v0.2.10in June).srlehn, is the only committer across the entire recent history. The repo is not archived and has ~289 stars.#111(Add Arc browser support) has been open since 2026-03-26 — four months.#110(Dia) likewise.Current coupling is small
Worth stating plainly, because it changes the cost of every option. The entire kooky surface is in
internal/cookies/cookies.go:kooky.ReadCookies(ctx, kooky.Valid, kooky.DomainHasSuffix("github.com"))— one call*kooky.Cookie→ localrawCookieinmapKookyCookies, already deliberately decoupled so selection logic is testable without kookybrowserReadHints— the fragile part, already flagged in a comment as needing re-verification on every bumpSo the abstraction boundary mostly exists already. What kooky actually provides is not API surface, it's per-platform decryption and store discovery: DPAPI/ABE on Windows, Keychain on macOS, libsecret/kwallet on Linux, plus Chromium and Firefox store formats. That is the part that's expensive to own.
Options
Options 1–3 all reduce dependence on kooky. Option 4 goes the other way — it keeps the dependency and removes the bottleneck instead.
0. Status quo. Keep consuming releases, send fixes upstream. Zero cost, no control over timing. Given the four-month backlog above, "no control over timing" is doing a lot of work in that sentence.
1. Vendor a subset into
internal/. Copy only the Chromium + Firefox read paths for the platforms we support. Full control, no release chain. Cost: we inherit ABE, Keychain, and keyring maintenance forever, including future Chrome encryption changes.2. Maintained fork (
drogers0/kooky) +replace. Cheap to start, lets #27 and #40 ship now. Cost: divergence and a permanent merge burden;replacedirectives in a released module are a smell.3. Switch libraries. Needs a survey of what else exists in Go and whether any handles ABE
v20.4. Become a kooky maintainer. Ask
srlehn/ thebrowserutilsorg for commit and release rights — either for me personally or for someone else willing to take it on. Rather than routing around the bottleneck, remove it. Release rights specifically matter: commit access without the ability to tag is only half the bottleneck.Not urgent.
GH_SESSION_TOKENis a complete escape hatch for every affected user today.