94 lines
3.9 KiB
Markdown
94 lines
3.9 KiB
Markdown
# scrot
|
|||
|
|
|
||
|
|
Clipboard sync across your own devices, and screenshot → instant public
|
||
|
|
link. One blob store, one small server, two thin CLI scripts.
|
||
|
|
|
||
|
|
## Why
|
||
|
|
|
||
|
|
Two small itches, solved by the same mechanism — see
|
||
|
|
[PROPOSAL.md](./PROPOSAL.md) for the full design rationale, and why this
|
||
|
|
isn't just `wisp` or `flit` again:
|
||
|
|
|
||
|
|
- **Clipboard sync**: copy on one machine, pull it on another, without a
|
||
|
|
cloud clipboard manager that might get paywalled or discontinued.
|
||
|
|
- **Screenshot → link**: capture a region, get a shareable URL on your
|
||
|
|
clipboard immediately, without an Imgur/CloudApp-style third-party
|
||
|
|
uploader.
|
||
|
|
|
||
|
|
## Model
|
||
|
|
|
||
|
|
A single `blobs` table with a `kind`:
|
||
|
|
|
||
|
|
- `clip` — private, text. Pull-only — your other devices ask "what's the
|
||
|
|
latest clip?" No public URL is ever minted.
|
||
|
|
- `shot` — public, image. Upload returns a short link (`/s/:id`) anyone
|
||
|
|
with the link can view — no auth required, same trust level as handing
|
||
|
|
someone a photo link.
|
||
|
|
|
||
|
|
Single shared `DEVICE_TOKEN` gates every write and the private clip-read —
|
||
|
|
this is single-user infrastructure across your own machines, not a
|
||
|
|
multi-tenant service, so there's no per-device registry to manage.
|
||
|
|
|
||
|
|
No end-to-end encryption: unlike `wisp` (built for sharing with other
|
||
|
|
people), this server is already fully trusted homelab infra, same trust
|
||
|
|
level as `keep`/`trace`. Adding client-side crypto here would defend
|
||
|
|
against a threat that doesn't apply.
|
||
|
|
|
||
|
|
## Server
|
||
|
|
|
||
|
|
```bash
|
||
|
|
cd server
|
||
|
|
npm install
|
||
|
|
DEVICE_TOKEN=... npm run dev
|
||
|
|
```
|
||
|
|
|
||
|
|
See [.env.example](./.env.example) for the full config surface
|
||
|
|
(`CLIP_TTL_SECONDS`, `SHOT_TTL_SECONDS`, `MAX_UPLOAD_BYTES`, ...).
|
||
|
|
|
||
|
|
## CLI
|
||
|
|
|
||
|
|
Four scripts in [cli/](./cli/), each meant to sit behind a keybinding:
|
||
|
|
|
||
|
|
- `scrot-clip-push` — pushes the current system clipboard as a `clip`.
|
||
|
|
- `scrot-clip-pull` — fetches the latest `clip` into the system clipboard.
|
||
|
|
- `scrot-shot` — captures a region screenshot, uploads it as a `shot`,
|
||
|
|
copies the resulting public link to the clipboard.
|
||
|
|
- `scrot-onboard` — prints (and, with `qrencode` installed, renders as a
|
||
|
|
terminal QR code) the link that onboards a phone onto the PWA below.
|
||
|
|
|
||
|
|
Each needs `SCROT_SERVER` and `DEVICE_TOKEN` in the environment — source
|
||
|
|
them from `~/.config/scrot/env` (gitignored, not tracked here). Wayland
|
||
|
|
(`wl-paste`/`wl-copy`/`grim`+`slurp`) and X11 (`xclip`/`maim`) are both
|
||
|
|
supported, auto-detected via `$WAYLAND_DISPLAY`.
|
||
|
|
|
||
|
|
## Mobile / PWA
|
||
|
|
|
||
|
|
The server also serves a small installable web app (`server/public/`) —
|
||
|
|
paste-or-share text as a `clip`, upload/share an image as a `shot`, and pull
|
||
|
|
the latest clip back down. No separate mobile client.
|
||
|
|
|
||
|
|
Onboarding a phone onto the shared `DEVICE_TOKEN`: run `scrot-onboard` and
|
||
|
|
open the printed link (or scan the QR) on the phone. That saves the token
|
||
|
|
into the page's `localStorage` and mirrors it into a cookie. Add the app to
|
||
|
|
the home screen and reopen it once — that re-fetches `/manifest.webmanifest`
|
||
|
|
with the token now baked into its `share_target.action` URL (see
|
||
|
|
[server/src/manifest.ts](./server/src/manifest.ts)), which is what makes
|
||
|
|
"Share to scrot" show up in the OS share sheet for photos and selected text.
|
||
|
|
No service worker involved: the share sheet POSTs straight to `/share`
|
||
|
|
([server/src/share.ts](./server/src/share.ts)), which checks the token in
|
||
|
|
the query string (custom headers aren't available to a share-target POST)
|
||
|
|
and inserts the blob through the same path as `POST /api/blobs`.
|
||
|
|
|
||
|
|
## Status
|
||
|
|
|
||
|
|
Scaffolded: server (routes, auth, TTL cleanup, SQLite, PWA share target) and
|
||
|
|
CLI scripts written, not yet run against a real deployment or wired into
|
||
|
|
`keep`. Next: `npm install` + smoke test locally, then a first deploy.
|
||
|
|
|
||
|
|
## Deploy
|
||
|
|
|
||
|
|
Mirrors the rest of the project family — `Dockerfile` + `docker-compose.yml`,
|
||
|
|
named volume for `data/`, env-driven config. Not yet migrated onto `keep`
|
||
|
|
for secret delivery (tracked in goonk's `FUTURE.md` ops migration list
|
||
|
|
alongside every other project still on hand-copied `.env`).
|