Recently Written · git

sbm-sync

Sync server for sbm bookmark files: one small Go program, plain files, AGPL

git clone https://github.com/equwal/sbm-sync

Log | Files | Refs


README.md (5013 bytes)

1 # sbm-sync
2 
3 sbm-sync keeps your [sbm](https://github.com/equwal/sbm) bookmark file the
4 same on each of your devices: bm on your computers, the sbm app for Android
5 and the sbm add-on for Firefox and Chrome.
6 
7 ![bm adds a page in the terminal; the add-on shows it at once and adds another, which bm then shows](demo/sbm-demo.gif)
8 
9 The demo (80 seconds, also as [MP4](demo/sbm-demo.mp4)): bm adds a bookmark
10 in a terminal. The add-on in the browser shows it, and adds the page that it
11 shows. After `bm-sync`, bm has that bookmark too. All through
12 sbm.subread.space.
13 
14 It is one small Go program. It keeps its data in plain files: no database.
15 The only dependency is `golang.org/x/crypto` for bcrypt.
16 
17 The clients use **https://sbm.subread.space** unless you set another
18 server. That server is free for 30 days, then $3 a month or $30 a year.
19 Run your own server free of charge.
20 
21 ## How sync works
22 
23 A device sends its whole bookmark file and the name of the version that it
24 got last time. The server merges the changes of the device into its own
25 copy, keeps the result and sends it back. The device writes the result to
26 its file and keeps the new version name.
27 
28 The merge compares whole lines:
29 
30 - A line that the device removed since its last version goes.
31 - A line that the device added comes in, at the end of the file, where bm
32   adds bookmarks. When the server has another line for the same bookmark
33   (the same URL, as bm compares URLs), the line of the device takes its
34   place. So an edit on a device wins.
35 - Lines that other devices added stay.
36 
37 When the server does not know the last version of a device (for its first
38 sync), nothing is removed: the result is the union of both files.
39 
40 ## Protocol
41 
42 Plain HTTP, for any client with curl.
43 
44     POST /api/login        form fields email, password
45                            200: a token, as text
46     POST /api/sync?base=V  Authorization: Bearer <token>
47                            body: the bookmark file
48                            200: the merged file; header Sbm-Version: <new V>
49     POST /api/logout       Authorization: Bearer <token>
50 
51 Errors come as text: 401 (sign in again), 402 (the subscription or the
52 trial has ended), 413 (the file is larger than 4 MB), 429 (too many sign-in
53 attempts).
54 
55     curl -d email=me@example.org --data-urlencode password=... https://sbm.subread.space/api/login
56     curl -H "Authorization: Bearer $token" --data-binary @bookmarks \
57         "https://sbm.subread.space/api/sync?base=$version"
58 
59 ## Run your own server
60 
61     go build
62     SBM_URL=https://sbm.example.org ./sbm-sync
63 
64 Then put it behind a web server that does TLS. `contrib/` has a systemd unit
65 and an nginx site. With Go installed elsewhere:
66 
67     GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build
68 
69 Settings, all through the environment:
70 
71     SBM_URL         public address of the server (default http://localhost:8750)
72     SBM_ADDR        address to listen on (default 127.0.0.1:8750)
73     SBM_DATA        data directory (default ./data)
74     SBM_CONTACT     email address on the privacy page (optional)
75     SBM_TRIAL_DAYS  days of sync before payment, with billing on (default 30)
76     SBM_BILLING_START  date when billing starts, as 2026-10-01: accounts
77                     from before it get the full trial from that date
78 
79 Accounts need no email check. To reset a password, the operator deletes the
80 account in `accounts.json` (with the server stopped), and the user signs up
81 again. The bookmark files stay on the devices.
82 
83 ## Billing
84 
85 Billing is off unless `STRIPE_SECRET_KEY` is set. Without billing, sync
86 is free for all accounts. To turn it on:
87 
88 1. In Stripe, make a product with two recurring prices, for example $3 a
89    month and $30 a year.
90 2. Add a webhook endpoint `https://<your server>/stripe` for the events
91    `checkout.session.completed`, `customer.subscription.created`,
92    `customer.subscription.updated` and `customer.subscription.deleted`.
93 3. Turn on the customer portal in the Stripe settings, so that customers
94    can cancel and change their card.
95 4. Set these variables, then restart the server:
96 
97         STRIPE_SECRET_KEY      sk_test_... or sk_live_...
98         STRIPE_WEBHOOK_SECRET  whsec_... of the endpoint
99         STRIPE_PRICE_MONTH     price_... of the monthly price
100         STRIPE_PRICE_YEAR      price_... of the yearly price
101 
102 Keep the keys out of git: put them in the environment file of the service.
103 
104 ## Data
105 
106     data/accounts.json            accounts: email, bcrypt hash, token hashes,
107                                   Stripe customer and subscription state
108     data/files/<account>/<sha256> the last 50 versions of each bookmark file
109     data/files/<account>/HEAD     name of the current version
110 
111 To back up, copy the directory.
112 
113 ## Test
114 
115     go vet ./... && go test ./...
116 
117 The merge has property tests (with [rapid](https://github.com/flyingmutant/rapid)).
118 
119 ## License
120 
121 AGPL-3.0. If you run a changed version for other people, give them its
122 source code.
123 
124 If sbm is useful to you, you can support it on [Ko-fi](https://ko-fi.com/truex).