Skip to content
Merged
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
28 changes: 23 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,18 @@ Your own cross-device image & text pool, deployable to Cloudflare's free tier in

**Product overview & setup guide:** https://shotsync-demo.defiabell.workers.dev/about

## Choose how to use ShotSync

**Self-hosting is the default: no ShotSync account, Supabase, D1 or email service is required.** Deploy to your Cloudflare account and enter the same access token on each device.

| Mode | Best for | Access | File storage |
| --- | --- | --- | --- |
| **Self-hosted (default)** | Your own image and text pool | Deploy, then enter your own `AUTH_TOKEN` | Your Cloudflare R2 |
| Hosted service (beta) | Using the operator's service without deploying | Register/sign in at the hosted URL | The operator's Cloudflare R2 |
| Read-only demo | Exploring the interface | Open the demo above; uploads are disabled | Public samples |

For self-hosting, follow [the steps below](#deploy-your-own-5-min). See the [hosted guide](docs/hosted.md) for availability and quotas: the current beta still has a Free-plan CPU limitation and its authentication upgrade is not deployed. [Mode details and FAQ](docs/deployment-modes.md).

![shotsync gallery](docs/screenshot.png)

## What it is
Expand Down Expand Up @@ -44,7 +56,7 @@ The dividing line is **a live transfer vs. a pool that waits**. LocalSend and Pa

**Choose shotsync if** you keep sending yourself screenshots and want them still there when you sit back down hours later, on a different machine, on a different network — and you would rather they lived in your own Cloudflare account than on a public image host. It replaces the habit of messaging things to yourself, not AirDrop.

**Do not choose shotsync if** you need per-user accounts: one shared token unlocks the whole pool. See the "Security model & limitations" section below before deploying.
**The default self-hosted mode has no per-user accounts:** one shared token unlocks the whole pool. Use the separate hosted mode if you need isolated accounts. See the "Security model & limitations" section below before deploying.

## Features

Expand All @@ -56,11 +68,11 @@ The dividing line is **a live transfer vs. a pool that waits**. LocalSend and Pa
- Single-token auth, constant-time compare, token never in URLs
- 30-day auto-retention via R2 lifecycle
- Runs entirely on the Cloudflare free tier (Workers + R2)
- ~50 tests (Vitest + `@cloudflare/vitest-pool-workers`)
- Tests (Vitest + `@cloudflare/vitest-pool-workers`)

## Deploy your own (~5 min)

Prereqs: a Cloudflare account, Node 18+, and **R2 enabled** (Dashboard → R2 → enable; Cloudflare asks for a card even on the free tier — the free allowance is not charged).
Prereqs: a Cloudflare account, Node.js 22+, and **R2 enabled** (Dashboard → R2 → enable; Cloudflare asks for a card even on the free tier — the free allowance is not charged).

```bash
git clone https://github.com/Defiabell/shotsync
Expand All @@ -73,14 +85,18 @@ npx wrangler r2 bucket create shotsync

# 2. set the shared access token — any long random string; you enter it on each device
openssl rand -hex 24 # generate one, copy it
npx wrangler secret put AUTH_TOKEN # paste it when prompted
npx wrangler secret put AUTH_TOKEN --config wrangler.toml --env "" # paste it when prompted

# 3. deploy
npm run deploy
```

You also need a **workers.dev subdomain** (Dashboard → Workers & Pages, one-time) or a custom domain. After deploy you get `https://shotsync.<your-subdomain>.workers.dev`.

`npm run deploy` explicitly uses `wrangler.toml` and deploys only the personal pool. It does not create an account database, request Supabase keys, or deploy the hosted service. `AUTH_TOKEN` is the ShotSync passphrase you generate, not a Cloudflare API token or Supabase key. Save it and share it only with people allowed to access the entire pool.

After deployment, open the same URL and enter the same token on two devices. Upload a short text snippet and check it appears on the other device. An email registration screen means you opened the hosted service; use the personal URL returned by Wrangler instead.

### 30-day retention

Dashboard → R2 → bucket `shotsync` → Settings → Object lifecycle rules → delete objects 30 days after creation.
Expand Down Expand Up @@ -125,7 +141,9 @@ Tap **`⚙`** in the top bar. The panel shows this pool's URL and the token this
- **`显示`** (Show) toggles the full token; **`复制`** (Copy) puts it on the clipboard for pasting into another device.
- **`退出登录`** (Log out) forgets the token on this device and returns to the token prompt. Nothing changes server-side; the same token still works elsewhere.

- **Single shared token.** Anyone with the URL **and** token can view/upload/delete. This is a single-user / trusted-circle tool, not multi-tenant — there are no per-user accounts and no way to "switch" tokens on one pool; a second pool is a second Worker deployment. Rotate with `npx wrangler secret put AUTH_TOKEN` — note this also invalidates all live share links, since the token is the link signing key.
## Security model & limitations

- **Single shared token.** Anyone with the URL **and** token can view/upload/delete. This is a single-user / trusted-circle tool, not multi-tenant — there are no per-user accounts and no way to "switch" tokens on one pool; a second pool is a second Worker deployment. Rotate with `npx wrangler secret put AUTH_TOKEN --config wrangler.toml --env ""` — note this also invalidates all live share links, since the token is the link signing key.
- **Share links are public** until they expire (7 days): anyone with the link can see that one item.
- **Transit pool, not an archive.** Items auto-delete after 30 days by design.
- **The UI is currently in Chinese.** i18n PRs welcome.
Expand Down
28 changes: 23 additions & 5 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,18 @@

**🎬 在线演示(只读示例池):https://shotsync-demo.defiabell.workers.dev**

## 选择使用方式

**自己部署是默认方式,不需要注册 ShotSync 账号,也不需要 Supabase、D1 或邮件服务。** 部署到自己的 Cloudflare 后,每台设备输入同一个访问令牌即可使用。

| 方式 | 适合谁 | 如何进入 | 数据存放 |
| --- | --- | --- | --- |
| **自己部署(默认)** | 想拥有自己的图片与文字池 | 部署后输入自己设置的 `AUTH_TOKEN` | 自己的 Cloudflare R2 |
| 公共托管服务(试用) | 不想部署,接受运营方存储数据和使用限额 | 在服务地址注册/登录 | 运营方的 Cloudflare R2 |
| 只读演示 | 先看看界面 | 打开上方演示地址;不能上传 | 公开样例 |

自部署按[下面的步骤](#自己部署约-5-分钟)操作即可。公共服务的可用状态和限额见[托管版说明](docs/hosted.md);托管版目前仍有免费 CPU 限制,认证升级尚未上线。[模式区别与常见问题](docs/deployment-modes.md)。

![shotsync gallery](docs/screenshot.png)

## 这是什么
Expand Down Expand Up @@ -42,7 +54,7 @@ iCloud / AirDrop / 网盘 / 公开图床,要么手动、要么锁死在某个

**这些情况用 shotsync**:你老是要把截图发给自己,希望几小时后换台机器、换个网络坐下来时它还在;并且比起公共图床,你更愿意让这些东西待在自己的 Cloudflare 账号里。它替代的是「用微信发给自己」这个习惯,不是替代 AirDrop。

**这些情况别用 shotsync**:你需要多用户各自的账号。整个池子只有一个共享 token部署前请先看下面的「安全模型与限制」。
**默认自部署模式不区分用户账号**:一个共享 token 解锁整个池子。需要用户隔离时使用独立的托管账号模式;部署前请先看下面的「安全模型与限制」。

## 功能

Expand All @@ -54,11 +66,11 @@ iCloud / AirDrop / 网盘 / 公开图床,要么手动、要么锁死在某个
- 单 token 鉴权、constant-time 比较、token 永不进 URL
- 30 天自动留存(R2 lifecycle)
- 完全跑在 Cloudflare 免费档(Workers + R2)
- ~50 个测试(Vitest + `@cloudflare/vitest-pool-workers`)
- 自动化测试(Vitest + `@cloudflare/vitest-pool-workers`)

## 自己部署(约 5 分钟)

前置:一个 Cloudflare 账户、Node 18+、并**启用 R2**(控制台 → R2 → 启用;即使免费档 Cloudflare 也会要求绑卡——免费额度内不扣费)。
前置:一个 Cloudflare 账户、Node.js 22+、并**启用 R2**(控制台 → R2 → 启用;即使免费档 Cloudflare 也会要求绑卡——免费额度内不扣费)。

```bash
git clone https://github.com/Defiabell/shotsync
Expand All @@ -71,14 +83,18 @@ npx wrangler r2 bucket create shotsync

# 2. 设置共享访问 token —— 任意长随机串;每台设备要输它
openssl rand -hex 24 # 生成一个,复制下来
npx wrangler secret put AUTH_TOKEN # 提示时粘贴
npx wrangler secret put AUTH_TOKEN --config wrangler.toml --env "" # 提示时粘贴

# 3. 部署
npm run deploy
```

你还需要一个 **workers.dev 子域名**(控制台 → Workers & Pages,一次性)或自定义域名。部署后会得到 `https://shotsync.<你的子域>.workers.dev`。

`npm run deploy` 固定使用 `wrangler.toml`,只部署个人池;不会创建账号数据库、要求 Supabase 密钥或部署公共账号服务。`AUTH_TOKEN` 是你自己生成的 ShotSync 访问口令,不是 Cloudflare API Token,也不是 Supabase 密钥。请保存它,并只交给允许访问整个池子的人。

部署后,在两台设备输入相同的地址和令牌,上传一小段文字并确认另一台能看到,即可完成检查。若出现邮箱注册页,你打开的是托管服务地址,请改用 Wrangler 刚返回的个人实例地址。

### 30 天自动清理

控制台 → R2 → 桶 `shotsync` → Settings → Object lifecycle rules → 对象创建 30 天后删除。
Expand Down Expand Up @@ -117,7 +133,9 @@ npm run deploy
- **`显示`** 切换明文;**`复制`** 放进剪贴板,拿去另一台设备粘贴。
- **`退出登录`** 只是让这台设备忘掉 token、回到输入页。服务端没有任何变化,同一个 token 在别的设备照常可用。

- **单一共享 token。** 拿到「地址 + token」的任何人都能看/传/删。这是单人 / 可信小圈子工具,不是多租户——没有按用户区分的账号,同一个池子也没有「切换 token」这回事;想要第二个池子就再部署一个 Worker。用 `npx wrangler secret put AUTH_TOKEN` 轮换——注意这会同时让所有现存分享链接失效(token 也是链接的签名密钥)。
## 安全模型与限制

- **单一共享 token。** 拿到「地址 + token」的任何人都能看/传/删。这是单人 / 可信小圈子工具,不是多租户——没有按用户区分的账号,同一个池子也没有「切换 token」这回事;想要第二个池子就再部署一个 Worker。用 `npx wrangler secret put AUTH_TOKEN --config wrangler.toml --env ""` 轮换——注意这会同时让所有现存分享链接失效(token 也是链接的签名密钥)。
- **分享链接是公开的**,直到过期(7 天):拿到链接的人都能看那一个 item。
- **中转池,不是归档。** item 按设计 30 天后自动删除。
- **界面目前是中文。** 欢迎提 i18n PR。
Expand Down
43 changes: 43 additions & 0 deletions docs/deployment-modes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# ShotSync:选择部署与使用方式 / Deployment modes

## 默认:部署自己的个人池

这是项目的主要使用方式。只需要自己的 Cloudflare 账号、一个 Worker、一个 R2 存储桶,以及自己生成的 `AUTH_TOKEN`。不需要注册 ShotSync 账号、Supabase、D1、邮件域名或验证码服务。

按 [中文快速部署](../README.zh-CN.md#自己部署约-5-分钟) 或 [English quick start](../README.md#deploy-your-own-5-min) 操作。`npm run deploy` 明确选择 `wrangler.toml`,入口是 `src/index.ts`;`npm run dev` 也使用同一配置。

部署成功后:

1. 保存 Wrangler 返回的个人实例地址及自己生成的访问令牌。
2. 每台设备打开该地址,输入相同令牌。令牌持有者共享整个池子,可查看、上传和删除。
3. 在一台设备发送一小段文字,检查另一台能看到。
4. 在 R2 配置 30 天删除规则。Worker 不会替你创建这条生命周期规则。

`AUTH_TOKEN` 是应用访问口令,**不要把 Cloudflare API Token、GitHub Token 或 Supabase Secret 填进网页**。令牌丢失时由部署者在自己的 Worker 上更换 `AUTH_TOKEN`;更换后各设备需重新输入,旧分享链接也会失效。

## 可选:直接使用公共托管服务

不想部署的用户可使用运营方提供的账号服务。浏览器通过邮箱和密码进入自己的文件池;文件存放在运营方的 Cloudflare 中,受服务人数、上传、存储和保留时间限制。使用者不需要开通 Cloudflare 或 Supabase。

托管服务与自部署实例的数据、登录账号和访问令牌不互通,不会自动迁移或同步。Mac App/快捷指令应填写所选服务的地址:个人池使用共享 `AUTH_TOKEN`,托管服务使用登录后创建的设备令牌。

当前公共服务仍为试用,原登录实现存在免费 CPU 限制,认证升级尚未部署;实际状态见[托管说明](hosted.md)。只读 demo 仅用于浏览公开样例,不接受上传,也不是公共账号服务。

## 仅运营者:部署多用户账号服务

[托管部署文档](hosted.md)面向想为多人运营服务的人。它有独立的 Worker 入口、D1 和 R2 资源;外部认证升级另需运营者的 Supabase 配置。不要把这套配置作为个人池的安装前提,也不要复制维护者的数据库 ID、存储桶或认证项目配置。

| 命令 | 配置 | 部署内容 |
| --- | --- | --- |
| `npm run deploy` | `wrangler.toml` | 无注册流程的个人 token 池(默认) |
| `npm run deploy:hosted` | `wrangler.hosted.jsonc` | 多用户账号服务,仅运营者使用 |

升级个人池代码不会自动开启账号系统。两种模式维持独立入口和显式配置;不根据是否发现 Supabase 密钥自动切换。只读 demo 是第三个独立演示环境。

## English summary

Self-hosting remains the default: Worker + R2 + one shared `AUTH_TOKEN`. Run `npm run deploy`; no ShotSync account, Supabase, D1, email or CAPTCHA setup is required. Token holders share the entire pool. Configure the 30-day R2 lifecycle explicitly.

The optional hosted service uses individual accounts and the operator's storage/quotas. Only its operator needs the extra infrastructure; end users sign in at that service's URL. Accounts, files and credentials are not automatically shared with personal deployments. The hosted beta is still undergoing an authentication upgrade; see its guide for current availability.

`npm run deploy:hosted` is an explicit, separate operator workflow. It never replaces the default personal deployment. The read-only demo is only for exploring sample content.
2 changes: 2 additions & 0 deletions docs/hosted.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Hosted ShotSync beta

> This guide is for operators running a multi-user service. To deploy a personal pool without accounts, use the [default README instructions](../README.md#deploy-your-own-5-min): Worker + R2 + `AUTH_TOKEN`, with no D1 or Supabase. See [mode selection](deployment-modes.md).

The hosted entry point (`src/hosted/index.ts`) adds email/password accounts with recovery codes and private per-account pools. It is a separate Worker, D1 database and R2 bucket. Existing personal deployments and the read-only demo retain their token-based behavior. The [hosted beta](https://shotsync-hosted.defiabell.workers.dev) is deployed on Workers Free; email delivery and a sender domain are not required.

## What people can do
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
"private": true,
"type": "module",
"scripts": {
"dev": "wrangler dev",
"deploy": "wrangler deploy",
"dev": "wrangler dev --config wrangler.toml --env \"\"",
"deploy": "wrangler deploy --config wrangler.toml --env \"\"",
"test": "vitest run",
"test:watch": "vitest",
"typecheck": "tsc --noEmit",
Expand Down
Loading
Loading