# gotohp: an unofficial Google Photos uploader for the desktop

> gotohp is a Go and Wails3 desktop client that uploads folders to Google Photos without the official client, with drag and drop, configurable threads, a CLI mode and a claim of unlimited uploads. It suits people moving a large personal library to Google Photos, and it carries the usual caveats of a reverse engineered tool that authenticates with your own session credentials.

**xob0t/gotohp** — Unofficial Google Photos Desktop GUI Client

- Repository: https://github.com/xob0t/gotohp
- Stars: 765 · Forks: 73
- Language: Go
- License: MIT
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/xob0t-gotohp

## A Google Photos client without the Google Photos client

gotohp describes itself as an unofficial Google Photos desktop GUI client, and the repository topics say the same thing in a more technical way: google-photos, uploader, gui, reverse-engineering, unofficial, unlimited-storage and wails3.

The goal is straightforward. Google Photos has a phone client with a limited upload allowance, and this is a desktop application that uploads your folders instead. The feature list leads with unlimited uploads, which can be disabled, followed by a drag and drop file upload interface, credential management, real-time upload progress tracking, configurable upload threads, uploads of individual files or directories with optional recursive scanning, skipping files already present in your account, and a CLI mode.

That last set of details is what separates this from a script that calls an API. The tool keeps a local record so that re-running it does not re-upload what you already have, it lets you watch progress, and it lets you set how many uploads run at once, which matters when you are pushing tens of thousands of files over a residential connection.

The licence is MIT, the language is Go, and the releases are published on the project's GitHub releases page, with a separate CLI artefact for people who would rather not open a window.

## The CLI and the GUI share a config file, not a behaviour

The most interesting design decision in the project is how the two front ends relate, and the README is precise about it.

The CLI shares the config file with the GUI, but reads only the stored credentials from it. Everything else, meaning the proxy, the quality setting and the upload behaviour, is set by flags, so a scripted run is unaffected by preferences changed in the GUI. That is a deliberate separation: a scheduled job should not inherit whatever someone last clicked in a window.

The usage is compact:

```shell
gotohp-cli upload /path/to/photos --recursive --threads 5
```

The recursive flag walks subdirectories, and threads sets the number of concurrent uploads. Two more flags control the rest: --config selects a different config file, and --account chooses which stored account to upload with, which matters as soon as you have more than one Google account in the credential store.

The GUI binary runs the same CLI when given a command, so gotohp upload ... and gotohp-cli upload ... are two entry points to one implementation rather than two implementations. The README points you to gotohp-cli help and gotohp-cli upload --help for the full option list.

The project is built with Wails 3, so the desktop shell is a web view around the Go core, and there is a second front end as well: the go.mod pulls in bubbletea, bubbles and lipgloss, which are the Charm libraries for a terminal user interface, and cobra for the command line surface. The result is a tool that can be driven three ways, from a window, from a terminal UI, or from a script.

## Where settings live and how to override them

Configuration is persistent, and the README describes the path in a form that is deliberately platform neutral. Settings are stored in a file called gotohp.config inside a gotohp directory under the system config path, and you can force a local config by creating an empty gotohp.config next to the executable.

That second sentence is the one that matters in practice. On a shared machine, or in a container, or when you want the settings to travel with the binary, the system config path is the wrong place. Creating an empty gotohp.config file beside the executable overrides it, and the --config flag does the same thing with an explicit path.

The configuration itself is managed with koanf, with providers for files, for structs and a YAML parser, which is a library chosen precisely for this job, since it lets the same key be set from a file, from a struct and from flags without the application caring where it came from. The upload settings are the ones the README calls configurable and persistent, alongside the proxy setting and the image quality option.

Credentials are stored in the same file, which is worth thinking about before you paste anything into it. The config holds the session data for your Google account, so the file permissions and whether it ends up in a backup or a synced folder are your problem, not the application's. A config file that lands in a cloud-synced folder is a credential in a cloud-synced folder.

## Three sign-in paths, and what they ask of you

The credential question is where an unofficial client has to do something unusual, and the README documents three options with a good deal of honesty about what each one involves.

The first is an embedded setup flow inside the application. You add a Google account, open the Google sign-in page, sign in, agree, and then open the browser developer tools, find the cookies for accounts.google.com, copy the value of the oauth_token cookie and paste it into gotohp. The README notes that the page may keep loading forever, and that this is expected.

The second option uses a patched Google Photos build on an Android device or emulator, with no root required. You install GmsCore and a patched APK, connect the device over ADB, and read the account identifier out of the device log, then supply that string as the credential. The README offers this as the option that avoids rooting.

The third uses an official APK and requires root, and it works by intercepting the authentication request with HTTP Toolkit over ADB, then copying the request body into gotohp. The troubleshooting section covers the case where no request was captured and the case where a token binding key is missing, and the tool can read that key from the device over ADB when it is asked to.

The common thread is that the application is not given a password. It is given a session credential that the user extracts from a device or a browser where they were already signed in. That is how unofficial clients work, and it is also the point where a careful user has to decide whether they are comfortable pasting session data into a third party binary, and whether they would rather use the official client.

## The Go module tells you what the tool is made of

The go.mod is short enough to read as an architecture summary, and it is more informative than the README in some ways.

The module is declared as app, and it requires Go 1.27.0, which tells you the toolchain floor before you try to build it. The core dependencies are Wails v3 at a beta release, cobra and pflag for the command line, koanf for configuration, protobuf for the wire format, and modernc.org/sqlite, which is the pure Go SQLite implementation, so there is no cgo dependency. The tink-crypto/tink-go library is there for cryptographic operations, and the Charm libraries bubbletea, bubbles, lipgloss and x/term provide the terminal interface.

Two of those deserve a comment. The Wails dependency is at v3.0.0-beta.21, so the desktop framework the project builds on is still a pre-release, and that is a reasonable reason to expect occasional breakage in the GUI layer even when the core is stable. The tink-crypto dependency is notable in a tool whose job involves credentials, since Tink is Google's library for key management and encrypted data, which suggests the credential values are not simply sitting in clear text in the config.

The repository layout follows the same split: core, internal, frontend and generated directories, a .proto directory for the protocol definitions, main.go and main_cli.go for the two entry points, version.go, a Taskfile.yml for task automation, .golangci.yml for linting, and release-please configuration for the release process, with an AGENTS.md at the root.

## Releases, and the practical limits of an unofficial client

The release history is short and recent: v0.9.0 on 2026-08-13, v0.10.0 on 2026-08-26 and v0.11.0 on 2026-09-14, with the last push on 2026-09-14. Version numbers below 1.0 with a beta framework dependency tell you where the project stands, which is a fine place for a personal tool and a poor place for a business dependency.

The limits are structural rather than bugs. There is no API contract, so a change on the Google side can break the tool at any time. The unlimited upload claim depends on how the service currently behaves rather than on anything the project controls. The credential methods depend on internal details of Google's sign-in flow and on patched Android builds, all of which can change without notice.

There is also a data point worth taking seriously: the tool skips files already present in your account, which means the local record of what you have uploaded has to be trusted. If that record is lost or the state is confused, you get duplicates, and if the state is wrong in the other direction you get files that were never uploaded and appear to be missing.

What gotohp does well is the part Google does not offer: a queue with progress, thread control, recursion, deduplication and a scriptable interface for a bulk import. For a one-time migration of a large library, that is a reasonable trade, and the MIT licence means you can read the code before you trust it with a credential.

## Conclusion

Use gotohp if you have a large photo library to move into Google Photos and you want a desktop client with a real upload queue, thread control and a scriptable CLI, since those are what the project actually provides. Do not use it if you need a supported, contractual path to Google Photos, because it is explicitly an unofficial reverse engineered client, and treat the sign-in step with care, since the documented methods involve handing the app a session credential from your own account. Before you start, read the credential options and pick the one that matches the device you have, keep the config file out of version control, and note that the latest release is v0.11.0 from 2026-09-14 and the last push was on the same day.

## FAQ

### What is gotohp?

gotohp is an unofficial Google Photos desktop GUI client written in Go with Wails 3. It provides unlimited uploads that can be disabled, a drag and drop interface, credential management, real-time upload progress, configurable upload threads, recursive directory uploads, skipping of files already in your account, and a CLI mode.

### How do I use the gotohp CLI?

Download the gotohp-cli release artefact for your platform and run gotohp-cli upload with a path, adding --recursive to walk subdirectories and --threads to set concurrency. The CLI shares the config file with the GUI but reads only stored credentials from it, so the proxy, quality and upload behaviour come from flags; --config picks a different config file and --account picks a stored account.

### Where does gotohp store its settings?

Configurable, persistent upload settings are stored in a file named gotohp.config inside a gotohp directory under the system config path. You can force a local config by creating an empty gotohp.config next to the executable, or pass --config with an explicit path.

## Sources

- [Issues](https://github.com/xob0t/gotohp/issues)
- [License: MIT](https://github.com/xob0t/gotohp/blob/main/LICENSE)
- [README](https://github.com/xob0t/gotohp/blob/main/README.md)
- [Releases](https://github.com/xob0t/gotohp/releases)
- [xob0t/gotohp on GitHub](https://github.com/xob0t/gotohp)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/xob0t-gotohp
