No description
Find a file
Natalie fd18da6d3b chore(quinn-desktop): commit dropzone + mcp package-lock
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 11:29:14 -04:00
deploy feat(tls): front dropzone with Caddy + Let's Encrypt via sslip.io 2026-06-28 08:37:38 -04:00
dropzone chore(quinn-desktop): commit dropzone + mcp package-lock 2026-06-29 11:29:14 -04:00
mcp chore(quinn-desktop): commit dropzone + mcp package-lock 2026-06-29 11:29:14 -04:00
.gitignore feat(quinn-desktop): dropzone droplet + MCP notifier 2026-06-28 08:16:09 -04:00
README.md feat: html format + publish_file (import attached files by path) 2026-06-28 11:56:09 -04:00

quinn-desktop

A quick, password-gated document dropzone for reviewing Claude Desktop cowork output on a phone — plus a local MCP server that publishes to it and texts you (iMessage) the one-tap link + password.

Claude Desktop (plum)
   └─ MCP "quinn-desktop"  ──POST /api/publish──▶  dropzone droplet (DO, bare IP)
        └─ osascript ──▶ iMessage to +14244663669:  "📄 New doc … <link> pw: xxxx"
                                                        ▲
                                            you tap the link on your phone

Two pieces:

Piece Runs on What it is
dropzone/ cheap DO droplet (bare IP) Node HTTP service, flat-JSON storage, Markdown render, one shared password
mcp/ plum, inside Claude Desktop MCP server: publish_document, list_documents + iMessage notify

How you use it (from Claude Desktop cowork)

  • Publish a doc: ask the assistant to publish_document with a title + body. format is markdown (default, rendered), html (raw HTML rendered verbatim), or text (shown as-is). You get an iMessage with a tap-through link and the password.
  • Import an attached file: publish_file with the file's absolute path — for data files attached to the conversation that aren't pasteable inline. Format is inferred from the extension (.md/.markdown, .html/.htm, else text) unless you override it.
  • Update a doc: publish again with the same title (or slug). The doc bumps a revision and you get an "✏️ Updated" text — that's the update alert.
  • See what's published: list_documents.

The texted link is http://<ip>/d/<slug>?k=<password> — one tap on the phone sets a 30-day cookie, so later visits to http://<ip>/ just work. The password is also texted separately for the login form.

Deploy the dropzone (one-time)

# 1. write the droplet secrets on plum (already done during bootstrap):
#    ~/.vault/quinn-dropzone.env   (see deploy/quinn-dropzone.env.example)

# 2. create the droplet (cocotte DO account, ~$4/mo):
deploy/provision.sh                 # prints IP, saves /tmp/quinn-dropzone-ip.txt

# 3. set PUBLIC_BASE_URL + PORT=8080 in ~/.vault/quinn-dropzone.env, then:
deploy/setup-remote.sh <ip>         # installs node, syncs code, starts systemd

# 4. front it with HTTPS (Caddy + auto Let's Encrypt, no DNS to manage):
deploy/setup-tls.sh <ip> <ip-with-dashes>.sslip.io
#   e.g. deploy/setup-tls.sh 134.209.175.97 134-209-175-97.sslip.io

Redeploy after code changes: deploy/setup-remote.sh <ip> again (idempotent).

sslip.io is a free public DNS resolver — no signup. <ip-with-dashes>.sslip.io already resolves to that IP for everyone, which gives Let's Encrypt a real hostname to issue a trusted cert against. Node binds :8080; Caddy owns :80/:443 and redirects to HTTPS.

MCP registration (Claude Desktop on plum)

Quit Claude Desktop fully, then run the installer:

osascript -e 'quit app "Claude"'        # make sure Desktop is closed
mcp/install-into-claude-desktop.sh      # merges the entry, reads secrets from ~/.vault
open -a Claude                          # launch — quinn-desktop now loads

Claude Desktop rewrites claude_desktop_config.json on quit and drops any entry added while it is running — so the installer must run while Desktop is closed. (Manual alternative: paste mcp/claude-desktop.snippet.json under mcpServers while quit, filling secrets from ~/.vault/quinn-dropzone.env.)

Required env: DROPZONE_BASE_URL, DROPZONE_TOKEN, VIEW_PASSWORD. Optional: QUINN_PHONE (default +14244663669), NOTIFY_METHOD (osascript default).

Notify path

NOTIFY_METHOD=osascript (default) sends the iMessage straight through Messages.app via mcp/send-imessage.applescript — reliable from a plum-local process, needs Automation permission for Messages (granted on first send).

NOTIFY_METHOD=macsync enqueues through the mac-sync server instead (MAC_SYNC_BASE_URL + MAC_SYNC_TOKEN). Use only once a mac-sync build that exposes /admin/send-queue/enqueue is actually deployed — the v0.4.0 server running on plum as of 2026-06-28 does not.

Security notes

  • HTTPS via Caddy + Let's Encrypt (134-209-175-97.sslip.io) — password and content are encrypted in transit. Still a single shared password, so treat it as a convenience review surface, not a vault.
  • One shared password + a long publish bearer token. Rotate by editing /etc/quinn-dropzone.env on the droplet (and the MCP env) and restarting.