Skip to content

Share URLs are too large for Slack/messaging apps (10-40KB+ URLs get truncated) #187

@GunitBindal

Description

@GunitBindal

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

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

  1. Fully backward compatible — existing hash-based URLs continue to work unchanged
  2. Graceful degradation — if paste service is unavailable, falls back silently to hash URLs
  3. Self-host friendly — configurable paste API URL via env var
  4. Privacy conscious — paste service is opt-in infrastructure, plans still only go where you send them
  5. 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() in packages/ui/utils/sharing.ts — POSTs to paste service with 5s timeout
  • loadFromPasteId() — fetches plan from paste ID on the portal
  • useSharing hook auto-generates short URLs with 1s debounce
  • ExportModal shows 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions