Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ A <code>demo-videos</code> skill publishes the <b>README demo reels</b> &mdash;

`gh-image` authenticates with credentials you already have — **nothing to provision, no OAuth scopes to configure**. Images and video going to a repository you can push to are uploaded with your `gh` CLI token; everything else — other file types, and repositories you cannot push to — falls back to your existing GitHub session, read as the `user_session` cookie from your browser's encrypted cookie store.

**Supported browsers:** Chrome · Brave · Chromium · Edge · Firefox · Opera · Safari
**Supported browsers:** Chrome · Brave · Chromium · Edge · Firefox · Opera · Safari · Zen

**Supported platforms:** macOS · Linux · Windows · Android (Termux)

Expand All @@ -179,7 +179,7 @@ gh image --token "$MY_TOKEN" screenshot.png --repo owner/repo
# Environment variable (preferred — not visible to `ps aux`)
GH_SESSION_TOKEN="$MY_TOKEN" gh image screenshot.png --repo owner/repo

# Non-standard browser not auto-detected (Firefox forks like Floorp/LibreWolf)?
# Non-standard browser not auto-detected (Firefox forks like Floorp/LibreWolf — Zen is auto-detected)?
GH_SESSION_TOKEN="$(sqlite3 ~/path/to/profile/cookies.sqlite "SELECT value FROM moz_cookies WHERE name='user_session' AND host LIKE '%github.com'")" \
gh image screenshot.png --repo owner/repo
```
Expand Down
14 changes: 12 additions & 2 deletions documentation/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,9 @@ Reads the GitHub `user_session` cookie from local browser cookie stores.
- AES decryption and cookie DB schema differences across versions
- Per-browser quirks for Chromium-family browsers, Firefox, Safari, and Opera

**Supported browsers** (registered via blank-imported kooky finders): Chrome, Brave, Edge, Chromium, Firefox, Opera, Safari. `GetGitHubSession` queries all of them in one pass, groups the `user_session` candidates per browser store, and prefers stores that are logged in. When more than one candidate survives, `validate` is used to pick a live one (pass nil to skip network validation).
**Supported browsers** (registered via blank-imported kooky finders): Chrome, Brave, Edge, Chromium, Firefox, Opera, Safari, and Zen. `GetGitHubSession` queries all of them in one pass, groups the `user_session` candidates per browser store, and prefers stores that are logged in. When more than one candidate survives, `validate` is used to pick a live one (pass nil to skip network validation).

Zen support requires an unreleased kooky version — see the `replace` directive in `go.mod` and the note in the Dependencies section below.

```go
// GetGitHubSession returns the best user_session cookie for github.com across
Expand Down Expand Up @@ -285,6 +287,14 @@ git push --tags
| Dependency | Purpose |
|---|---|
| [`browserutils/kooky`](https://github.com/browserutils/kooky) | Cross-browser cookie extraction (Keychain / DPAPI / Keyring + AES + SQLite/ESE) |

Zen support (`browser/zen`) isn't in a tagged kooky release yet — [browserutils/kooky#118](https://github.com/browserutils/kooky/pull/118) adds it upstream. Until that merges and ships in a release, `go.mod` carries a `replace` directive pointing at the PR branch (as a resolved pseudo-version, so builds are reproducible without depending on the branch still existing):

```
replace github.com/browserutils/kooky => github.com/jeremy-albinet/kooky v0.2.11-0.20260813144856-cee8d34a9d6b
```

**Remove this `replace` once kooky#118 merges and tags a release**, then `go get -u github.com/browserutils/kooky` and run `go mod tidy`.
| Go standard library `net/http` | HTTP client + cookie jar for the upload flow |
| Go standard library `mime/multipart` | Multipart form construction |
| Go standard library `encoding/json` | JSON parsing |
Expand All @@ -293,7 +303,7 @@ git push --tags
## Platform Notes

- **macOS:** Fully supported. On first browser-cookie use, a Keychain prompt may appear to authorize access to the browser's cookie encryption key. Click "Always Allow" to avoid repeated prompts. Safari is supported in addition to Chromium-family browsers.
- **Linux:** Supported via kooky (GNOME Keyring / kwallet for Chromium-family key storage; Firefox profile DBs read directly). The upload flow is platform-agnostic.
- **Linux:** Supported via kooky (GNOME Keyring / kwallet for Chromium-family key storage; Firefox and Zen profile DBs read directly). The upload flow is platform-agnostic.
- **Windows:** Supported via kooky (DPAPI for Chromium-family cookie decryption). Binaries are built for Windows amd64.
- **CI / headless environments:** Use `GH_SESSION_TOKEN` (preferred) or `--token` to skip browser extraction entirely.

Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,5 @@ require (
golang.org/x/text v0.34.0 // indirect
gopkg.in/ini.v1 v1.67.1 // indirect
)

replace github.com/browserutils/kooky => github.com/jeremy-albinet/kooky v0.2.11-0.20260813144856-cee8d34a9d6b
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
github.com/browserutils/ese v0.0.0-20260314233042-37b6a03a93ce h1:xb/LXUukZgVLMRnTUyEiCfMNH7KUCFOS4aOZnc/N+H8=
github.com/browserutils/ese v0.0.0-20260314233042-37b6a03a93ce/go.mod h1:Rj9TJxm7cExxJmdec83sr8cjvyF3raBsszTFviSo/6U=
github.com/browserutils/kooky v0.2.10 h1:hEgbFJHf9lkMlSr0YdVEGtEo1yvqaTcGXNx0meNBgBA=
github.com/browserutils/kooky v0.2.10/go.mod h1:ndMF+xzEi4voUsZ4dX0BL45oompbf5wqBaKR+J3lUNk=
github.com/browserutils/sqlite3 v0.0.2 h1:RDSivmwoS5DauKYxh06G4Y+m6IX3da7Cq9Jh5x+oIUA=
github.com/browserutils/sqlite3 v0.0.2/go.mod h1:3i7CY1ba3/D+qovGRJyCgfDnu/lGc8sg8ieM6jIUO50=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
Expand All @@ -11,6 +9,8 @@ github.com/godbus/dbus/v5 v5.2.2 h1:TUR3TgtSVDmjiXOgAAyaZbYmIeP3DPkld3jgKGV8mXQ=
github.com/godbus/dbus/v5 v5.2.2/go.mod h1:3AAv2+hPq5rdnr5txxxRwiGjPXamgoIHgz9FPBfOp3c=
github.com/gonuts/binary v0.2.0 h1:caITwMWAoQWlL0RNvv2lTU/AHqAJlVuu6nZmNgfbKW4=
github.com/gonuts/binary v0.2.0/go.mod h1:kM+CtBrCGDSKdv8WXTuCUsw+loiy8f/QEI8YCCC0M/E=
github.com/jeremy-albinet/kooky v0.2.11-0.20260813144856-cee8d34a9d6b h1:cKGw3vW25SLoV/tAbumJUAx1oS47zVRWd3UeOC/QgUI=
github.com/jeremy-albinet/kooky v0.2.11-0.20260813144856-cee8d34a9d6b/go.mod h1:ndMF+xzEi4voUsZ4dX0BL45oompbf5wqBaKR+J3lUNk=
github.com/keybase/go-keychain v0.0.1 h1:way+bWYa6lDppZoZcgMbYsvC7GxljxrskdNInRtuthU=
github.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXwkPPMeUgOK1k=
github.com/pierrec/lz4/v4 v4.1.26 h1:GrpZw1gZttORinvzBdXPUXATeqlJjqUG/D87TKMnhjY=
Expand Down
3 changes: 2 additions & 1 deletion internal/cookies/cookies.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
_ "github.com/browserutils/kooky/browser/firefox"
_ "github.com/browserutils/kooky/browser/opera"
_ "github.com/browserutils/kooky/browser/safari"
_ "github.com/browserutils/kooky/browser/zen"
)

// NewSessionCookie builds a github.com user_session cookie from a raw value.
Expand Down Expand Up @@ -52,7 +53,7 @@ type sessionCandidate struct {
const noSessionMsg = "no github.com user_session cookie found in any supported " +
"browser — are you logged into GitHub? Set GH_SESSION_TOKEN to supply the " +
"cookie manually, or log into GitHub in Chrome, Chromium, Edge, Firefox, " +
"Brave, Opera, or Safari."
"Brave, Opera, Safari, or Zen."

// browserReadHints maps a substring that may appear in a browser-read error to
// actionable guidance. Matching is by substring, not errors.Is: the triggering
Expand Down