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.
Files changed (52) hide show
  1. cync_cli-0.3.0/.dockerignore +16 -0
  2. cync_cli-0.3.0/.env.example +36 -0
  3. cync_cli-0.3.0/.github/workflows/publish.yml +28 -0
  4. cync_cli-0.3.0/.gitignore +11 -0
  5. cync_cli-0.3.0/Dockerfile +13 -0
  6. cync_cli-0.3.0/LICENSE +21 -0
  7. cync_cli-0.3.0/PKG-INFO +176 -0
  8. cync_cli-0.3.0/PRIVACY.md +61 -0
  9. cync_cli-0.3.0/README.md +122 -0
  10. cync_cli-0.3.0/client/.env.example +3 -0
  11. cync_cli-0.3.0/client/.gitignore +23 -0
  12. cync_cli-0.3.0/client/.npmrc +1 -0
  13. cync_cli-0.3.0/client/.vscode/extensions.json +3 -0
  14. cync_cli-0.3.0/client/README.md +29 -0
  15. cync_cli-0.3.0/client/package.json +28 -0
  16. cync_cli-0.3.0/client/pnpm-lock.yaml +1535 -0
  17. cync_cli-0.3.0/client/pnpm-workspace.yaml +2 -0
  18. cync_cli-0.3.0/client/src/app.d.ts +16 -0
  19. cync_cli-0.3.0/client/src/app.html +12 -0
  20. cync_cli-0.3.0/client/src/hooks.server.ts +38 -0
  21. cync_cli-0.3.0/client/src/lib/assets/favicon.svg +1 -0
  22. cync_cli-0.3.0/client/src/lib/index.ts +1 -0
  23. cync_cli-0.3.0/client/src/lib/parse.ts +53 -0
  24. cync_cli-0.3.0/client/src/lib/server/cync.ts +14 -0
  25. cync_cli-0.3.0/client/src/lib/types.ts +23 -0
  26. cync_cli-0.3.0/client/src/routes/+layout.server.ts +10 -0
  27. cync_cli-0.3.0/client/src/routes/+layout.svelte +130 -0
  28. cync_cli-0.3.0/client/src/routes/+layout.ts +25 -0
  29. cync_cli-0.3.0/client/src/routes/+page.server.ts +15 -0
  30. cync_cli-0.3.0/client/src/routes/+page.svelte +22 -0
  31. cync_cli-0.3.0/client/src/routes/auth/callback/+server.ts +15 -0
  32. cync_cli-0.3.0/client/src/routes/auth/signout/+server.ts +7 -0
  33. cync_cli-0.3.0/client/src/routes/login/+page.svelte +35 -0
  34. cync_cli-0.3.0/client/src/routes/p/[slug]/+page.server.ts +17 -0
  35. cync_cli-0.3.0/client/src/routes/p/[slug]/+page.svelte +21 -0
  36. cync_cli-0.3.0/client/src/routes/p/[slug]/c/[id]/+page.server.ts +40 -0
  37. cync_cli-0.3.0/client/src/routes/p/[slug]/c/[id]/+page.svelte +28 -0
  38. cync_cli-0.3.0/client/src/routes/privacy/+page.svelte +31 -0
  39. cync_cli-0.3.0/client/static/robots.txt +3 -0
  40. cync_cli-0.3.0/client/tsconfig.json +20 -0
  41. cync_cli-0.3.0/client/vite.config.ts +20 -0
  42. cync_cli-0.3.0/pyproject.toml +52 -0
  43. cync_cli-0.3.0/scripts/migrate_legacy.py +119 -0
  44. cync_cli-0.3.0/src/cync/__init__.py +3 -0
  45. cync_cli-0.3.0/src/cync/auth.py +184 -0
  46. cync_cli-0.3.0/src/cync/client.py +319 -0
  47. cync_cli-0.3.0/src/cync/common.py +185 -0
  48. cync_cli-0.3.0/src/cync/config.py +108 -0
  49. cync_cli-0.3.0/src/cync/server.py +261 -0
  50. cync_cli-0.3.0/src/cync/storage.py +98 -0
  51. cync_cli-0.3.0/src/cync/supabase_store.py +199 -0
  52. cync_cli-0.3.0/supabase/schema.sql +50 -0
@@ -0,0 +1,16 @@
1
+ client/
2
+ scripts/
3
+ supabase/
4
+ .venv/
5
+ .git/
6
+ .github/
7
+ __pycache__/
8
+ **/__pycache__/
9
+ *.pyc
10
+ *.egg-info/
11
+ .env
12
+ .env.*
13
+ node_modules/
14
+ .svelte-kit/
15
+ .vercel/
16
+ .DS_Store
@@ -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,11 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.pyc
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ .env
8
+ .DS_Store
9
+ node_modules/
10
+ .svelte-kit/
11
+ web/build/
@@ -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.
@@ -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**.
@@ -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,3 @@
1
+ PUBLIC_SUPABASE_URL=https://<ref>.supabase.co
2
+ PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key
3
+ CYNC_SERVER_URL=http://127.0.0.1:8787
@@ -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,3 @@
1
+ {
2
+ "recommendations": ["svelte.svelte-vscode"]
3
+ }
@@ -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
+ }