main README.md
Eric Bower  ·  2026-02-25
  1# patchbin
  2
  3A pastebin for patches, supercharged for git collaboration.
  4
  5Contributions are designed to be anonymous: the quality of your work is what matters. No signup required, just connect with an SSH key.
  6
  7The target project doesn't need to run patchbin for someone to submit a patch request against it. It works like a pull request, except both sides collaborate by sending rounds of patchsets: a contributor sends patches, a reviewer replies with their own patches on top, back and forth, as commits rather than comments. The result is a collaborative workspace built entirely out of patches. Reviewing means pulling the code down, not clicking through a diff viewer. Issues work the same way: an issue is just a patch request without any code attached yet, and anyone can follow up with a real patch request on top of it.
  8
  9There's no accept or reject step. A patch request is simply active or inactive: active ones go inactive after 30 days without activity. When a reviewer is happy with the code, they pull it, merge it, and push upstream themselves; there's nothing to manage here beyond that.
 10
 11## quickstart
 12
 13Submit a patch request (starts as a draft, visible only to you):
 14
 15```
 16git format-patch main --stdout | ssh {url} pr create {repo}
 17```
 18
 19Open it so others can see it (also enables RSS notifications):
 20
 21```
 22ssh {url} pr open {prID}
 23```
 24
 25Checkout the latest patchset from a patch request:
 26
 27```
 28ssh {url} print pr-{prID} | git am -3
 29```
 30
 31Add a follow-up patchset (e.g. after addressing review comments):
 32
 33```
 34git format-patch main --stdout | ssh {url} pr add {prID}
 35```
 36
 37Help guide:
 38
 39```
 40ssh {url} help
 41```
 42
 43## commands
 44
 45### pr - manage patch requests
 46
 47- `pr create {repo}` - submit a new PR from stdin (starts as draft)
 48  ```
 49  git format-patch main --stdout | ssh {url} pr create {repo}
 50  ```
 51- `pr add {prID}` - add a new patchset to an existing PR from stdin
 52  ```
 53  git format-patch main --stdout | ssh {url} pr add {prID}
 54  ```
 55- `pr open {prID} [--comment]` - transition draft open, enables RSS notifications
 56  ```
 57  ssh {url} pr open {prID}
 58  ```
 59- `pr draft {prID} [--comment]` - transition open draft, disables RSS notifications
 60  ```
 61  ssh {url} pr draft {prID}
 62  ```
 63- `pr edit {prID} {title}` - rename a PR
 64  ```
 65  ssh {url} pr edit {prID} "new title"
 66  ```
 67- `pr summary {prID}` - show metadata, patchsets, and patches for a PR
 68  ```
 69  ssh {url} pr summary {prID}
 70  ```
 71- `pr ls [repo] [--draft|--open|--active|--inactive|--mine]` - list PRs
 72  ```
 73  ssh {url} pr ls {repo} --open
 74  ```
 75
 76### issue - text-only patch requests
 77
 78- `issue create {repo} [--title]` - submit a new issue from stdin (starts as open)
 79  ```
 80  echo "steps to reproduce..." | ssh {url} issue create {repo} --title "bug: crash on startup"
 81  ```
 82
 83### ps - manage patchsets
 84
 85- `ps rm {patchsetID}` - remove a patchset and its patches (creator only)
 86  ```
 87  ssh {url} ps rm ps-{patchsetID}
 88  ```
 89
 90### print - print patches for checkout
 91
 92- `print pr-{prID}` - print the latest patchset for a PR
 93  ```
 94  ssh {url} print pr-{prID} | git am -3
 95  ```
 96- `print ps-{patchsetID}` - print a specific patchset
 97  ```
 98  ssh {url} print ps-{patchsetID} | git am -3
 99  ```
100
101### logs - event history
102
103- `logs [--pr ID] [--pubkey]` - list event logs, optionally filtered to a PR or your own activity
104  ```
105  ssh {url} logs --pr {prID}
106  ```
107
108## self-hosting
109
110patchbin needs a `patchbin.toml` config file and a data directory (for the sqlite db and SSH host keys).
111
112[Copy](./patchbin.toml) or create a `patchbin.toml` file inside a `./data` directory:
113
114```
115mkdir -p data
116cp patchbin.toml ./data/patchbin.toml
117vim ./data/patchbin.toml
118```
119
120### docker-compose
121
122The included `docker-compose.yml` pulls the published image and mounts a local data directory:
123
124```
125services:
126  patchbin:
127    image: ghcr.io/picosh/pico/patchbin:latest
128    restart: always
129    volumes:
130      - ./data/patchbin/data:/app/data
131```
132
133Place `patchbin.toml` inside `./data/patchbin/data`, then run:
134
135```
136docker compose up -d
137```
138
139### docker image
140
141Run the image directly, mounting your data directory to `/app/data`:
142
143```
144docker run -d -v ./data:/app/data ghcr.io/picosh/pico/patchbin:latest
145```
146
147`patchbin.toml` must live inside the mounted `./data` directory, since that's the default config path the binary looks for.
148
149### from go source
150
151Clone the repo, then build and run the binary:
152
153```
154make build
155./build/patchbin --config ./data/patchbin.toml
156```
157
158Or without the Makefile:
159
160```
161go build -o ./build/patchbin ./cmd/patchbin
162./build/patchbin --config ./data/patchbin.toml
163```
164
165### done
166
167Access the SSH app:
168
169```
170ssh -p 2222 localhost help
171```
172
173Access the web app:
174
175```
176curl localhost:3000
177```