-
Notifications
You must be signed in to change notification settings - Fork 136
Description
Problem
Plannotator encodes the entire plan + annotations into the URL hash using deflate compression + base64url. For any non-trivial plan, this creates URLs that are 10-40KB+.
These URLs get silently truncated by:
- Slack — truncates URLs over ~4KB, link appears clickable but opens a broken/demo plan
- WhatsApp — truncates long URLs in messages
- Twitter/X — URL length limits in posts
- Email clients — many wrap or break long URLs
- iMessage — can truncate in previews
- GitHub comments/issues — renders but may break on copy
This is the #1 barrier to team sharing — the core collaboration feature effectively doesn't work for real-world plans shared via common messaging tools.
Related
- [BUG][UPDATED TITLE] When a share URL is truncated/corrupted, the app silently falls back to the demo plan #142 — Silent fallback to demo plan when share URLs are truncated (symptom of this issue)
Current Architecture
Plan + Annotations -> JSON -> deflate-raw compress -> base64url -> URL hash
Result: https://share.plannotator.ai/#<10-40KB of compressed data>
The share portal is a static SPA with no backend — all data lives in the URL. This is elegant for privacy but creates unusable URLs for sharing.
Proposed Solution: Optional Paste Service for Short URLs
Add a lightweight paste service that stores the compressed payload server-side and returns a short URL:
Plan + Annotations -> compress -> POST /api/paste -> { id: "aBcDeFgH" }
Result: https://share.plannotator.ai/p/aBcDeFgH (~50 chars, always)
Design Principles
- Fully backward compatible — existing hash-based URLs continue to work unchanged
- Graceful degradation — if paste service is unavailable, falls back silently to hash URLs
- Self-host friendly — configurable paste API URL via env var
- Privacy conscious — paste service is opt-in infrastructure, plans still only go where you send them
- 90-day TTL — pastes auto-expire, no unbounded storage growth
Implementation
Paste Worker (apps/paste-worker/):
- Cloudflare Worker with KV storage (essentially free at Plannotator's scale)
POST /api/paste— stores compressed data, returns{ id, url }GET /api/paste/:id— retrieves stored data- 512KB payload limit, 90-day TTL, CORS configured for share portal
Client Changes (all backward compatible):
createShortShareUrl()inpackages/ui/utils/sharing.ts— POSTs to paste service with 5s timeoutloadFromPasteId()— fetches plan from paste ID on the portaluseSharinghook auto-generates short URLs with 1s debounceExportModalshows short URL as primary copy target, full hash URL as backup- Portal detects
/p/<id>paths and loads from paste service automatically
UX Change
Before (Export Modal — Share tab):
Shareable URL
[https://share.plannotator.ai/#eNrtXW1v28YS_isEWqAp...] 42.3 KB
After (Export Modal — Share tab):
Share Link
[https://share.plannotator.ai/p/aBcDeFgH] [Copy]
Short link - safe for Slack, email, and messaging apps. Expires in 90 days.
Full URL (backup)
[https://share.plannotator.ai/#eNrtXW1v28YS_isEWqAp...] 42.3 KB
Alternatives Considered
| Approach | Why Not |
|---|---|
| Better compression (brotli, zstd) | Only saves 10-20%, URLs still 8-30KB |
| URL shortener (bit.ly, tinyurl) | External dependency, privacy concerns, rate limits |
| Binary protocol instead of JSON | High complexity, marginal gains |
| Truncate plan in URL | Defeats the purpose — recipient sees incomplete plan |
Deployment
The paste worker needs to be deployed to paste.plannotator.ai with a Cloudflare KV namespace. It is a single file (~130 lines) with zero dependencies.