cync-cli 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cync_cli-0.3.0/.dockerignore +16 -0
- cync_cli-0.3.0/.env.example +36 -0
- cync_cli-0.3.0/.github/workflows/publish.yml +28 -0
- cync_cli-0.3.0/.gitignore +11 -0
- cync_cli-0.3.0/Dockerfile +13 -0
- cync_cli-0.3.0/LICENSE +21 -0
- cync_cli-0.3.0/PKG-INFO +176 -0
- cync_cli-0.3.0/PRIVACY.md +61 -0
- cync_cli-0.3.0/README.md +122 -0
- cync_cli-0.3.0/client/.env.example +3 -0
- cync_cli-0.3.0/client/.gitignore +23 -0
- cync_cli-0.3.0/client/.npmrc +1 -0
- cync_cli-0.3.0/client/.vscode/extensions.json +3 -0
- cync_cli-0.3.0/client/README.md +29 -0
- cync_cli-0.3.0/client/package.json +28 -0
- cync_cli-0.3.0/client/pnpm-lock.yaml +1535 -0
- cync_cli-0.3.0/client/pnpm-workspace.yaml +2 -0
- cync_cli-0.3.0/client/src/app.d.ts +16 -0
- cync_cli-0.3.0/client/src/app.html +12 -0
- cync_cli-0.3.0/client/src/hooks.server.ts +38 -0
- cync_cli-0.3.0/client/src/lib/assets/favicon.svg +1 -0
- cync_cli-0.3.0/client/src/lib/index.ts +1 -0
- cync_cli-0.3.0/client/src/lib/parse.ts +53 -0
- cync_cli-0.3.0/client/src/lib/server/cync.ts +14 -0
- cync_cli-0.3.0/client/src/lib/types.ts +23 -0
- cync_cli-0.3.0/client/src/routes/+layout.server.ts +10 -0
- cync_cli-0.3.0/client/src/routes/+layout.svelte +130 -0
- cync_cli-0.3.0/client/src/routes/+layout.ts +25 -0
- cync_cli-0.3.0/client/src/routes/+page.server.ts +15 -0
- cync_cli-0.3.0/client/src/routes/+page.svelte +22 -0
- cync_cli-0.3.0/client/src/routes/auth/callback/+server.ts +15 -0
- cync_cli-0.3.0/client/src/routes/auth/signout/+server.ts +7 -0
- cync_cli-0.3.0/client/src/routes/login/+page.svelte +35 -0
- cync_cli-0.3.0/client/src/routes/p/[slug]/+page.server.ts +17 -0
- cync_cli-0.3.0/client/src/routes/p/[slug]/+page.svelte +21 -0
- cync_cli-0.3.0/client/src/routes/p/[slug]/c/[id]/+page.server.ts +40 -0
- cync_cli-0.3.0/client/src/routes/p/[slug]/c/[id]/+page.svelte +28 -0
- cync_cli-0.3.0/client/src/routes/privacy/+page.svelte +31 -0
- cync_cli-0.3.0/client/static/robots.txt +3 -0
- cync_cli-0.3.0/client/tsconfig.json +20 -0
- cync_cli-0.3.0/client/vite.config.ts +20 -0
- cync_cli-0.3.0/pyproject.toml +52 -0
- cync_cli-0.3.0/scripts/migrate_legacy.py +119 -0
- cync_cli-0.3.0/src/cync/__init__.py +3 -0
- cync_cli-0.3.0/src/cync/auth.py +184 -0
- cync_cli-0.3.0/src/cync/client.py +319 -0
- cync_cli-0.3.0/src/cync/common.py +185 -0
- cync_cli-0.3.0/src/cync/config.py +108 -0
- cync_cli-0.3.0/src/cync/server.py +261 -0
- cync_cli-0.3.0/src/cync/storage.py +98 -0
- cync_cli-0.3.0/src/cync/supabase_store.py +199 -0
- cync_cli-0.3.0/supabase/schema.sql +50 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# ---- cync server (.env) ----
|
|
2
|
+
|
|
3
|
+
# Blob storage backend: "r2" (default) or "local" (filesystem, for testing)
|
|
4
|
+
CYNC_STORAGE=r2
|
|
5
|
+
# CYNC_LOCAL_DIR=~/.cync-store # only used when CYNC_STORAGE=local
|
|
6
|
+
|
|
7
|
+
# Cloudflare R2 (transcript blobs) — R2 dashboard -> Manage R2 API Tokens
|
|
8
|
+
# Create a token with Object Read & Write.
|
|
9
|
+
R2_ACCOUNT_ID=your_cloudflare_account_id
|
|
10
|
+
R2_ACCESS_KEY_ID=your_r2_access_key_id
|
|
11
|
+
R2_SECRET_ACCESS_KEY=your_r2_secret_access_key
|
|
12
|
+
R2_BUCKET=cync
|
|
13
|
+
# Optional: set R2_ENDPOINT directly instead of R2_ACCOUNT_ID
|
|
14
|
+
# R2_ENDPOINT=https://<account_id>.r2.cloudflarestorage.com
|
|
15
|
+
|
|
16
|
+
# Supabase (GitHub auth + Postgres metadata) — Project Settings -> API
|
|
17
|
+
SUPABASE_URL=https://<ref>.supabase.co
|
|
18
|
+
SUPABASE_ANON_KEY=your_supabase_anon_key
|
|
19
|
+
SUPABASE_SERVICE_ROLE_KEY=your_supabase_service_role_key
|
|
20
|
+
|
|
21
|
+
# Where the server listens
|
|
22
|
+
CYNC_HOST=127.0.0.1
|
|
23
|
+
CYNC_PORT=8787
|
|
24
|
+
# CYNC_MAX_BODY_BYTES=67108864 # 64 MB request-body cap
|
|
25
|
+
|
|
26
|
+
# ---- hosted-service policy (optional) ----
|
|
27
|
+
# Access: restrict to specific GitHub emails/usernames (comma-separated). Empty = open.
|
|
28
|
+
# CYNC_ALLOWLIST=you@example.com,yourgithubusername
|
|
29
|
+
# Per-user quotas (public/open mode):
|
|
30
|
+
# CYNC_MAX_PROJECTS=20
|
|
31
|
+
# CYNC_MAX_BYTES_PER_USER=524288000 # 500 MB per user
|
|
32
|
+
|
|
33
|
+
# ---- CLI (each machine) ----
|
|
34
|
+
# Point the CLI at your server (env var, or ~/.config/cync/config.toml `server_url`).
|
|
35
|
+
# Must be https:// except on localhost.
|
|
36
|
+
# CYNC_SERVER_URL=http://127.0.0.1:8787
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
name: publish
|
|
2
|
+
|
|
3
|
+
# Publish the CLI to PyPI when a version tag (v*) is pushed.
|
|
4
|
+
# Uses PyPI Trusted Publishing (OIDC) — no API token stored in the repo.
|
|
5
|
+
# One-time PyPI setup: pypi.org -> project cync-cli -> Publishing -> add a
|
|
6
|
+
# GitHub trusted publisher (owner: 03hgryan, repo: cync, workflow: publish.yml).
|
|
7
|
+
|
|
8
|
+
on:
|
|
9
|
+
push:
|
|
10
|
+
tags: ["v*"]
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
publish:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
permissions:
|
|
16
|
+
contents: read # actions/checkout needs repo read access
|
|
17
|
+
id-token: write # required for PyPI trusted publishing (OIDC)
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v4
|
|
20
|
+
- uses: actions/setup-python@v5
|
|
21
|
+
with:
|
|
22
|
+
python-version: "3.12"
|
|
23
|
+
- name: Build sdist + wheel
|
|
24
|
+
run: |
|
|
25
|
+
python -m pip install --upgrade build
|
|
26
|
+
python -m build
|
|
27
|
+
- name: Publish to PyPI
|
|
28
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# cync API server (FastAPI) — container for Cloud Run / any Docker host.
|
|
2
|
+
FROM python:3.12-slim
|
|
3
|
+
|
|
4
|
+
WORKDIR /app
|
|
5
|
+
|
|
6
|
+
# Install the package + server extras (fastapi, uvicorn, boto3, pydantic).
|
|
7
|
+
COPY pyproject.toml ./
|
|
8
|
+
COPY src ./src
|
|
9
|
+
RUN pip install --no-cache-dir ".[server]"
|
|
10
|
+
|
|
11
|
+
ENV CYNC_STORAGE=r2
|
|
12
|
+
# Cloud Run injects $PORT (defaults to 8080); bind all interfaces.
|
|
13
|
+
CMD ["sh", "-c", "uvicorn cync.server:app --host 0.0.0.0 --port ${PORT:-8080}"]
|
cync_cli-0.3.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 03hgryan
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
cync_cli-0.3.0/PKG-INFO
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cync-cli
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: Sync Claude Code conversations across machines — GitHub-authed CLI + server (Postgres metadata, R2 blobs).
|
|
5
|
+
Project-URL: Homepage, https://github.com/03hgryan/cync
|
|
6
|
+
Project-URL: Repository, https://github.com/03hgryan/cync
|
|
7
|
+
Author: 03hgryan
|
|
8
|
+
License: MIT License
|
|
9
|
+
|
|
10
|
+
Copyright (c) 2026 03hgryan
|
|
11
|
+
|
|
12
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
13
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
14
|
+
in the Software without restriction, including without limitation the rights
|
|
15
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
16
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
17
|
+
furnished to do so, subject to the following conditions:
|
|
18
|
+
|
|
19
|
+
The above copyright notice and this permission notice shall be included in all
|
|
20
|
+
copies or substantial portions of the Software.
|
|
21
|
+
|
|
22
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
23
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
24
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
25
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
26
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
27
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
28
|
+
SOFTWARE.
|
|
29
|
+
License-File: LICENSE
|
|
30
|
+
Keywords: claude,claude-code,cli,conversations,sync
|
|
31
|
+
Classifier: Environment :: Console
|
|
32
|
+
Classifier: Intended Audience :: Developers
|
|
33
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
34
|
+
Classifier: Programming Language :: Python :: 3
|
|
35
|
+
Classifier: Topic :: Utilities
|
|
36
|
+
Requires-Python: >=3.11
|
|
37
|
+
Requires-Dist: httpx<1,>=0.27
|
|
38
|
+
Requires-Dist: python-dotenv<2,>=1.0
|
|
39
|
+
Requires-Dist: rich<15,>=13
|
|
40
|
+
Requires-Dist: typer<1,>=0.12
|
|
41
|
+
Provides-Extra: dev
|
|
42
|
+
Requires-Dist: boto3<2,>=1.34; extra == 'dev'
|
|
43
|
+
Requires-Dist: fastapi<1,>=0.110; extra == 'dev'
|
|
44
|
+
Requires-Dist: pydantic<3,>=2; extra == 'dev'
|
|
45
|
+
Requires-Dist: pytest<9,>=8; extra == 'dev'
|
|
46
|
+
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
47
|
+
Requires-Dist: uvicorn[standard]<1,>=0.29; extra == 'dev'
|
|
48
|
+
Provides-Extra: server
|
|
49
|
+
Requires-Dist: boto3<2,>=1.34; extra == 'server'
|
|
50
|
+
Requires-Dist: fastapi<1,>=0.110; extra == 'server'
|
|
51
|
+
Requires-Dist: pydantic<3,>=2; extra == 'server'
|
|
52
|
+
Requires-Dist: uvicorn[standard]<1,>=0.29; extra == 'server'
|
|
53
|
+
Description-Content-Type: text/markdown
|
|
54
|
+
|
|
55
|
+
# cync — code sync for Claude Code conversations
|
|
56
|
+
|
|
57
|
+
Sync your Claude Code conversations across machines, signed in with **GitHub**.
|
|
58
|
+
Your conversation **metadata** lives in your **Supabase** Postgres (per-user, RLS),
|
|
59
|
+
the **transcripts** live in your **Cloudflare R2** bucket, and a small FastAPI
|
|
60
|
+
server brokers both. Like git, but for your chats: `cync push` on one machine,
|
|
61
|
+
`cync pull` on another, and `claude --resume` picks up where you left off — plus
|
|
62
|
+
a web viewer to browse them.
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
Browser / CLI ── GitHub login (Supabase Auth) ──┐
|
|
66
|
+
▼ JWT
|
|
67
|
+
FastAPI ── Postgres (metadata, per-user RLS)
|
|
68
|
+
── R2 (transcript blobs)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## How it works
|
|
72
|
+
- Sign in once per machine with `cync login` (GitHub via Supabase, browser PKCE).
|
|
73
|
+
- A **project** is a first-class record you own; create one with `cync init <name>`
|
|
74
|
+
and link a directory to it. Conversations are keyed by their Claude session UUID.
|
|
75
|
+
- `push` uploads `~/.claude/projects/<repo>/<id>.jsonl`; `pull` writes it back on the
|
|
76
|
+
other machine and rewrites the embedded `cwd` so `claude --resume` just works.
|
|
77
|
+
- Everything is scoped to your GitHub user — projects/conversations are private to you.
|
|
78
|
+
|
|
79
|
+
## Install & use (hosted)
|
|
80
|
+
|
|
81
|
+
The CLI is on PyPI as **`cync-cli`** (the command is `cync`). No server setup —
|
|
82
|
+
it points at the hosted cync by default:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
pipx install cync-cli # or: uv tool install cync-cli
|
|
86
|
+
cync login # sign in with GitHub
|
|
87
|
+
cd ~/path/to/your/project
|
|
88
|
+
cync init myproject && cync push # machine A
|
|
89
|
+
cync link myproject && cync pull # machine B → claude --resume
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Self-hosting
|
|
93
|
+
|
|
94
|
+
Prefer your own stack? Point the CLI at your deployment via `CYNC_SERVER_URL`
|
|
95
|
+
(env or `~/.config/cync/config.toml`), then deploy the server + provision your
|
|
96
|
+
own Supabase + R2:
|
|
97
|
+
|
|
98
|
+
### 1. Provision (one time)
|
|
99
|
+
- **Cloudflare R2:** create a bucket (default name `cync`) and an API token with
|
|
100
|
+
**Object Read & Write**.
|
|
101
|
+
- **Supabase:** create a project, then run [`supabase/schema.sql`](supabase/schema.sql)
|
|
102
|
+
in the SQL editor. Enable **Auth → Providers → GitHub** (create a GitHub OAuth App
|
|
103
|
+
with callback `https://<ref>.supabase.co/auth/v1/callback`). Under **Auth → URL
|
|
104
|
+
Configuration**, add redirect URLs `http://127.0.0.1:8765/callback` (CLI) and
|
|
105
|
+
`http://localhost:5173/auth/callback` (web).
|
|
106
|
+
|
|
107
|
+
### 2. Server
|
|
108
|
+
```bash
|
|
109
|
+
uv venv && source .venv/bin/activate
|
|
110
|
+
uv pip install -e ".[server]"
|
|
111
|
+
cp .env.example .env # fill in R2_* and SUPABASE_* (URL, anon, service_role)
|
|
112
|
+
cync-server # http://127.0.0.1:8787
|
|
113
|
+
```
|
|
114
|
+
> For cross-machine use, expose the server over **HTTPS** (Tailscale, Fly.io, Railway).
|
|
115
|
+
> The CLI refuses non-HTTPS server URLs except on localhost.
|
|
116
|
+
|
|
117
|
+
### 3. CLI (each machine)
|
|
118
|
+
```bash
|
|
119
|
+
uv pip install -e . # base install
|
|
120
|
+
export CYNC_SERVER_URL=http://127.0.0.1:8787 # or your https URL / config.toml
|
|
121
|
+
cync login # sign in with GitHub (opens a browser)
|
|
122
|
+
|
|
123
|
+
cd ~/path/to/your/project
|
|
124
|
+
cync init droneforge # create + link a project (machine A)
|
|
125
|
+
cync push
|
|
126
|
+
# on machine B: cync link droneforge && cync pull → claude --resume
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### 4. Web viewer (optional)
|
|
130
|
+
```bash
|
|
131
|
+
cd client
|
|
132
|
+
pnpm install
|
|
133
|
+
cp .env.example .env # PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY, CYNC_SERVER_URL
|
|
134
|
+
pnpm dev # http://localhost:5173 → "Sign in with GitHub"
|
|
135
|
+
```
|
|
136
|
+
The SvelteKit server holds the token (Supabase SSR cookies); browse at `/p/<project>`.
|
|
137
|
+
|
|
138
|
+
## Commands
|
|
139
|
+
| Command | What it does |
|
|
140
|
+
|---|---|
|
|
141
|
+
| `cync login` / `logout` / `whoami` | GitHub auth session |
|
|
142
|
+
| `cync init <name>` | create a project + link this directory |
|
|
143
|
+
| `cync link <name>` | link this directory to an existing project |
|
|
144
|
+
| `cync unlink` | remove this directory's link |
|
|
145
|
+
| `cync push [id]` | upload this project's conversation(s) |
|
|
146
|
+
| `cync pull [id]` | download into `~/.claude` (overwrite/add) |
|
|
147
|
+
| `cync list` | list this project's conversations |
|
|
148
|
+
| `cync project list` / `rm <name>` | manage your projects |
|
|
149
|
+
|
|
150
|
+
## Migrating from v0.2
|
|
151
|
+
If you have v0.2 (static-token) data still in R2, after `cync login`:
|
|
152
|
+
```bash
|
|
153
|
+
python scripts/migrate_legacy.py --slug <name> --email <your-github-email> [--purge]
|
|
154
|
+
```
|
|
155
|
+
Or simply re-`cync push` from a machine that has the conversations locally.
|
|
156
|
+
|
|
157
|
+
## Security
|
|
158
|
+
- Per-user GitHub auth (Supabase JWT); every project/conversation is owner-scoped,
|
|
159
|
+
with Postgres RLS as a backstop. The server holds R2 + Supabase keys; clients never do.
|
|
160
|
+
- CLI login uses PKCE + a single-use loopback callback; the session (refresh token)
|
|
161
|
+
is stored `0600` at `~/.config/cync/session.json`.
|
|
162
|
+
- Request bodies are capped (`CYNC_MAX_BODY_BYTES`, default 64 MB).
|
|
163
|
+
|
|
164
|
+
## Layout
|
|
165
|
+
```
|
|
166
|
+
src/cync/ server (FastAPI), CLI, auth, Supabase + R2 stores
|
|
167
|
+
client/ SvelteKit web viewer (GitHub login)
|
|
168
|
+
supabase/ Postgres schema + RLS
|
|
169
|
+
scripts/ legacy migration
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Limitations
|
|
173
|
+
- Last-writer-wins; no version history yet.
|
|
174
|
+
- Syncs the `.jsonl` transcript only (enough for `--resume`); not `todos/` sidecars.
|
|
175
|
+
- Don't `push` a conversation that's open in Claude Code — quit first so it isn't mid-write.
|
|
176
|
+
- Listing reads one object per conversation from Postgres (fine for personal scale).
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# cync — Privacy Policy
|
|
2
|
+
|
|
3
|
+
_Last updated: 2026-07-02. This is a starting template — review and adapt it (and
|
|
4
|
+
consult a professional) before relying on it for a public service._
|
|
5
|
+
|
|
6
|
+
cync ("the service") syncs your Claude Code conversations across machines. This
|
|
7
|
+
explains what it stores and how the data is handled.
|
|
8
|
+
|
|
9
|
+
## What we collect
|
|
10
|
+
|
|
11
|
+
- **Identity:** when you sign in with GitHub (via Supabase Auth), we store your
|
|
12
|
+
GitHub account id, email, and username.
|
|
13
|
+
- **Conversations you push:** the Claude Code conversation transcripts you choose
|
|
14
|
+
to `cync push` — stored as files in Cloudflare R2, with metadata (title, size,
|
|
15
|
+
timestamps, project) in Supabase Postgres.
|
|
16
|
+
|
|
17
|
+
We do **not** access your GitHub repositories or anything beyond your basic
|
|
18
|
+
profile and the conversations you explicitly push.
|
|
19
|
+
|
|
20
|
+
## ⚠️ Conversation content
|
|
21
|
+
|
|
22
|
+
Transcripts can contain whatever you and Claude typed — source code, file paths,
|
|
23
|
+
and potentially **secrets or personal data**. Only push conversations you're
|
|
24
|
+
comfortable storing. You are responsible for the content you upload.
|
|
25
|
+
|
|
26
|
+
## Who can see your data
|
|
27
|
+
|
|
28
|
+
- Your projects and conversations are **isolated per user** by database
|
|
29
|
+
row-level security — other users cannot access them.
|
|
30
|
+
- The service operator has **technical access** to the underlying storage (via
|
|
31
|
+
administrative keys) as needed to operate, maintain, and debug the service. The
|
|
32
|
+
operator does not view your conversation content except as necessary to run the
|
|
33
|
+
service or to comply with the law.
|
|
34
|
+
|
|
35
|
+
## Where it's stored
|
|
36
|
+
|
|
37
|
+
- **Cloudflare R2** (conversation files) and **Supabase Postgres** (metadata +
|
|
38
|
+
auth), both encrypted at rest by those providers. Hosting: **Google Cloud Run**.
|
|
39
|
+
|
|
40
|
+
## Retention & deletion
|
|
41
|
+
|
|
42
|
+
- Delete a project and all its conversations any time with `cync project rm <name>`.
|
|
43
|
+
- To delete your account and all associated data, contact **gimogunn@gmail.com**.
|
|
44
|
+
|
|
45
|
+
## Third parties
|
|
46
|
+
|
|
47
|
+
We rely on GitHub (authentication), Supabase (auth + metadata), Cloudflare R2
|
|
48
|
+
(storage), and Google Cloud (hosting). We do **not** sell your data, and there is
|
|
49
|
+
no advertising.
|
|
50
|
+
|
|
51
|
+
## Cookies
|
|
52
|
+
|
|
53
|
+
The web viewer sets a Supabase authentication cookie solely to keep you signed in.
|
|
54
|
+
|
|
55
|
+
## Changes
|
|
56
|
+
|
|
57
|
+
We may update this policy; material changes are reflected by the date above.
|
|
58
|
+
|
|
59
|
+
## Contact
|
|
60
|
+
|
|
61
|
+
Questions or deletion requests: **gimogunn@gmail.com**.
|
cync_cli-0.3.0/README.md
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# cync — code sync for Claude Code conversations
|
|
2
|
+
|
|
3
|
+
Sync your Claude Code conversations across machines, signed in with **GitHub**.
|
|
4
|
+
Your conversation **metadata** lives in your **Supabase** Postgres (per-user, RLS),
|
|
5
|
+
the **transcripts** live in your **Cloudflare R2** bucket, and a small FastAPI
|
|
6
|
+
server brokers both. Like git, but for your chats: `cync push` on one machine,
|
|
7
|
+
`cync pull` on another, and `claude --resume` picks up where you left off — plus
|
|
8
|
+
a web viewer to browse them.
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
Browser / CLI ── GitHub login (Supabase Auth) ──┐
|
|
12
|
+
▼ JWT
|
|
13
|
+
FastAPI ── Postgres (metadata, per-user RLS)
|
|
14
|
+
── R2 (transcript blobs)
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## How it works
|
|
18
|
+
- Sign in once per machine with `cync login` (GitHub via Supabase, browser PKCE).
|
|
19
|
+
- A **project** is a first-class record you own; create one with `cync init <name>`
|
|
20
|
+
and link a directory to it. Conversations are keyed by their Claude session UUID.
|
|
21
|
+
- `push` uploads `~/.claude/projects/<repo>/<id>.jsonl`; `pull` writes it back on the
|
|
22
|
+
other machine and rewrites the embedded `cwd` so `claude --resume` just works.
|
|
23
|
+
- Everything is scoped to your GitHub user — projects/conversations are private to you.
|
|
24
|
+
|
|
25
|
+
## Install & use (hosted)
|
|
26
|
+
|
|
27
|
+
The CLI is on PyPI as **`cync-cli`** (the command is `cync`). No server setup —
|
|
28
|
+
it points at the hosted cync by default:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pipx install cync-cli # or: uv tool install cync-cli
|
|
32
|
+
cync login # sign in with GitHub
|
|
33
|
+
cd ~/path/to/your/project
|
|
34
|
+
cync init myproject && cync push # machine A
|
|
35
|
+
cync link myproject && cync pull # machine B → claude --resume
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Self-hosting
|
|
39
|
+
|
|
40
|
+
Prefer your own stack? Point the CLI at your deployment via `CYNC_SERVER_URL`
|
|
41
|
+
(env or `~/.config/cync/config.toml`), then deploy the server + provision your
|
|
42
|
+
own Supabase + R2:
|
|
43
|
+
|
|
44
|
+
### 1. Provision (one time)
|
|
45
|
+
- **Cloudflare R2:** create a bucket (default name `cync`) and an API token with
|
|
46
|
+
**Object Read & Write**.
|
|
47
|
+
- **Supabase:** create a project, then run [`supabase/schema.sql`](supabase/schema.sql)
|
|
48
|
+
in the SQL editor. Enable **Auth → Providers → GitHub** (create a GitHub OAuth App
|
|
49
|
+
with callback `https://<ref>.supabase.co/auth/v1/callback`). Under **Auth → URL
|
|
50
|
+
Configuration**, add redirect URLs `http://127.0.0.1:8765/callback` (CLI) and
|
|
51
|
+
`http://localhost:5173/auth/callback` (web).
|
|
52
|
+
|
|
53
|
+
### 2. Server
|
|
54
|
+
```bash
|
|
55
|
+
uv venv && source .venv/bin/activate
|
|
56
|
+
uv pip install -e ".[server]"
|
|
57
|
+
cp .env.example .env # fill in R2_* and SUPABASE_* (URL, anon, service_role)
|
|
58
|
+
cync-server # http://127.0.0.1:8787
|
|
59
|
+
```
|
|
60
|
+
> For cross-machine use, expose the server over **HTTPS** (Tailscale, Fly.io, Railway).
|
|
61
|
+
> The CLI refuses non-HTTPS server URLs except on localhost.
|
|
62
|
+
|
|
63
|
+
### 3. CLI (each machine)
|
|
64
|
+
```bash
|
|
65
|
+
uv pip install -e . # base install
|
|
66
|
+
export CYNC_SERVER_URL=http://127.0.0.1:8787 # or your https URL / config.toml
|
|
67
|
+
cync login # sign in with GitHub (opens a browser)
|
|
68
|
+
|
|
69
|
+
cd ~/path/to/your/project
|
|
70
|
+
cync init droneforge # create + link a project (machine A)
|
|
71
|
+
cync push
|
|
72
|
+
# on machine B: cync link droneforge && cync pull → claude --resume
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### 4. Web viewer (optional)
|
|
76
|
+
```bash
|
|
77
|
+
cd client
|
|
78
|
+
pnpm install
|
|
79
|
+
cp .env.example .env # PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY, CYNC_SERVER_URL
|
|
80
|
+
pnpm dev # http://localhost:5173 → "Sign in with GitHub"
|
|
81
|
+
```
|
|
82
|
+
The SvelteKit server holds the token (Supabase SSR cookies); browse at `/p/<project>`.
|
|
83
|
+
|
|
84
|
+
## Commands
|
|
85
|
+
| Command | What it does |
|
|
86
|
+
|---|---|
|
|
87
|
+
| `cync login` / `logout` / `whoami` | GitHub auth session |
|
|
88
|
+
| `cync init <name>` | create a project + link this directory |
|
|
89
|
+
| `cync link <name>` | link this directory to an existing project |
|
|
90
|
+
| `cync unlink` | remove this directory's link |
|
|
91
|
+
| `cync push [id]` | upload this project's conversation(s) |
|
|
92
|
+
| `cync pull [id]` | download into `~/.claude` (overwrite/add) |
|
|
93
|
+
| `cync list` | list this project's conversations |
|
|
94
|
+
| `cync project list` / `rm <name>` | manage your projects |
|
|
95
|
+
|
|
96
|
+
## Migrating from v0.2
|
|
97
|
+
If you have v0.2 (static-token) data still in R2, after `cync login`:
|
|
98
|
+
```bash
|
|
99
|
+
python scripts/migrate_legacy.py --slug <name> --email <your-github-email> [--purge]
|
|
100
|
+
```
|
|
101
|
+
Or simply re-`cync push` from a machine that has the conversations locally.
|
|
102
|
+
|
|
103
|
+
## Security
|
|
104
|
+
- Per-user GitHub auth (Supabase JWT); every project/conversation is owner-scoped,
|
|
105
|
+
with Postgres RLS as a backstop. The server holds R2 + Supabase keys; clients never do.
|
|
106
|
+
- CLI login uses PKCE + a single-use loopback callback; the session (refresh token)
|
|
107
|
+
is stored `0600` at `~/.config/cync/session.json`.
|
|
108
|
+
- Request bodies are capped (`CYNC_MAX_BODY_BYTES`, default 64 MB).
|
|
109
|
+
|
|
110
|
+
## Layout
|
|
111
|
+
```
|
|
112
|
+
src/cync/ server (FastAPI), CLI, auth, Supabase + R2 stores
|
|
113
|
+
client/ SvelteKit web viewer (GitHub login)
|
|
114
|
+
supabase/ Postgres schema + RLS
|
|
115
|
+
scripts/ legacy migration
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Limitations
|
|
119
|
+
- Last-writer-wins; no version history yet.
|
|
120
|
+
- Syncs the `.jsonl` transcript only (enough for `--resume`); not `todos/` sidecars.
|
|
121
|
+
- Don't `push` a conversation that's open in Claude Code — quit first so it isn't mid-write.
|
|
122
|
+
- Listing reads one object per conversation from Postgres (fine for personal scale).
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
node_modules
|
|
2
|
+
|
|
3
|
+
# Output
|
|
4
|
+
.output
|
|
5
|
+
.vercel
|
|
6
|
+
.netlify
|
|
7
|
+
.wrangler
|
|
8
|
+
/.svelte-kit
|
|
9
|
+
/build
|
|
10
|
+
|
|
11
|
+
# OS
|
|
12
|
+
.DS_Store
|
|
13
|
+
Thumbs.db
|
|
14
|
+
|
|
15
|
+
# Env
|
|
16
|
+
.env
|
|
17
|
+
.env.*
|
|
18
|
+
!.env.example
|
|
19
|
+
!.env.test
|
|
20
|
+
|
|
21
|
+
# Vite
|
|
22
|
+
vite.config.js.timestamp-*
|
|
23
|
+
vite.config.ts.timestamp-*
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
engine-strict=true
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# cync-viewer
|
|
2
|
+
|
|
3
|
+
A small read-only SvelteKit app to browse Claude Code conversations synced by
|
|
4
|
+
[cync](../). The SvelteKit server proxies to the cync API and injects the bearer
|
|
5
|
+
token server-side, so the token never reaches the browser.
|
|
6
|
+
|
|
7
|
+
## Setup
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
pnpm install
|
|
11
|
+
cp .env.example .env # set CYNC_SERVER_URL and the same CYNC_TOKEN as the server
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Develop
|
|
15
|
+
|
|
16
|
+
```sh
|
|
17
|
+
pnpm dev # http://localhost:5173
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Build
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
pnpm build
|
|
24
|
+
pnpm preview
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
> This project uses **pnpm** (see `pnpm-lock.yaml`); avoid mixing in `npm install`.
|
|
28
|
+
> For a real deployment, replace `@sveltejs/adapter-auto` with a pinned adapter
|
|
29
|
+
> such as `@sveltejs/adapter-node`.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "cync-viewer",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "0.0.1",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "vite dev",
|
|
8
|
+
"build": "vite build",
|
|
9
|
+
"preview": "vite preview",
|
|
10
|
+
"prepare": "svelte-kit sync || echo ''",
|
|
11
|
+
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
|
12
|
+
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"@sveltejs/adapter-vercel": "^6.3.4",
|
|
16
|
+
"@sveltejs/kit": "^2.63.0",
|
|
17
|
+
"@sveltejs/vite-plugin-svelte": "^7.1.2",
|
|
18
|
+
"@types/node": "^26.0.1",
|
|
19
|
+
"svelte": "^5.56.1",
|
|
20
|
+
"svelte-check": "^4.6.0",
|
|
21
|
+
"typescript": "^6.0.3",
|
|
22
|
+
"vite": "^8.0.16"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@supabase/ssr": "^0.12.0",
|
|
26
|
+
"@supabase/supabase-js": "^2.109.0"
|
|
27
|
+
}
|
|
28
|
+
}
|