Skip to content

Latest commit

 

History

History
149 lines (117 loc) · 8.37 KB

File metadata and controls

149 lines (117 loc) · 8.37 KB

CLAUDE.md — R2Drop Agent Context

Self-updating document. Update this file whenever you learn something new about the codebase, discover a pattern, fix a non-obvious bug, or make an architectural decision. This file is your persistent memory across sessions.

Project Intent

R2Drop is a native macOS menu bar application for uploading files and folders to Cloudflare R2 storage. Users right-click files in Finder → "Send to R2" and the app handles multipart, parallel, resumable uploads. The project is open source (MIT) and models itself after the Tailscale macOS experience: minimal UI, reliable background operation, zero friction.

The companion CLI (r2-cli) shares the same Rust upload engine and config, enabling headless/terminal use on macOS (Apple Silicon and Intel).

Repo Layout

r2drop/                           # Repo root (this directory)
├── .github/workflows/            # CI/CD workflows
│   ├── release.yml               # Release: sign, notarize, publish signed Sparkle appcast + .dmg
│   ├── cli-release.yml           # CLI binary release
│   ├── deploy-www.yml            # Deploy website to R2
│   └── ci.yml                    # PR: lint, build, test
├── CLAUDE.md                     # ← You are here. Keep this updated.
├── AGENTS.md                     # Agent operating instructions (must stay in sync with CLAUDE.md)
├── LICENSE                       # MIT License
├── app/                          # Xcode workspace root
│   ├── R2Drop.xcworkspace/       # Open this in Xcode
│   ├── R2Drop/                   # Xcode project (SwiftUI app + extensions)
│   ├── Packages/
│   │   ├── R2Core/               # Swift: models, config, queue, history
│   │   └── R2Bridge/             # Swift: FFI wrapper around Rust
│   ├── engine/                   # Rust workspace
│   │   ├── r2-core/              # Shared upload logic, S3 client
│   │   ├── r2-ffi/               # C FFI bridge (staticlib + cbindgen)
│   │   └── r2-cli/               # Standalone CLI binary
│   └── scripts/                  # Build scripts
├── www/                          # Marketing website (deployed to R2)
├── gitbook/                      # GitBook documentation (synced to docs.r2drop.com)
├── scripts/                      # Global scripts (install.sh)
└── homebrew/                     # Homebrew tap templates

Tech Stack

  • UI: Swift + SwiftUI (macOS 13+, menu bar app)
  • Upload engine: Rust (async, tokio, aws-sdk-s3)
  • FFI: Rust staticlib + cbindgen → C header → Swift via R2Bridge package
  • Database: SQLite (rusqlite) for queue.db and history.db
  • Config: TOML at ~/.r2drop/config.toml
  • Credentials: macOS Keychain (Security.framework), service: com.superhumancorp.r2drop
  • IPC: App Groups (group.com.superhumancorp.r2drop) + shared SQLite
  • Auto-updates: Sparkle framework (Ed25519 key: NWlOpvs7+ccCaW6557MqyCO94w3KVziS7uAOOxR8gQk=)
  • CI/CD: GitHub Actions
  • Distribution: .dmg from GitHub Releases (Homebrew tap at github.com/superhumancorp/homebrew-tap)

Credentials & Secrets

NEVER commit secrets. All paths below are gitignored.

Location Contents
app/.env CF_API_TOKEN, GITHUB_TOKEN, APPLE_CERTIFICATE_BASE64, APPLE_CERTIFICATE_PASSWORD, APPLE_TEAM_ID, APPLE_ID (email), APPLE_APP_SPECIFIC_PASSWORD, optional SPARKLE_ED25519_KEY
app/credentials/ Apple .cer files, CSR, provisioning profiles
GitHub Actions Secrets Mirror of .env values — release also requires SPARKLE_ED25519_KEY (preferred) or SPARKLE_PRIVATE_KEY

Runtime credentials (user's R2 tokens) are stored exclusively in macOS Keychain. The config.toml contains account metadata (bucket names, endpoints) but never tokens or secrets.

Local Data Directory

~/.r2drop/
├── config.toml       # Accounts, preferences (NO secrets)
├── queue.db          # Upload queue (SQLite)
├── history.db        # Upload history (SQLite)
├── r2drop.sock       # Unix socket for CLI ↔ app IPC
└── logs/
    └── r2drop.log    # Rolling log files

Key Architectural Decisions

  • Upload-only: No download, sync, or browsing of bucket contents (P0 scope)
  • No Cloudflare OAuth: Cloudflare doesn't support third-party desktop OAuth. Auth is a guided one-time token paste → Keychain storage flow.
  • Finder Sync Extension communicates with the main app via App Groups shared SQLite — not XPC
  • TOML config shared between macOS app and CLI so both read ~/.r2drop/config.toml
  • Rust staticlib (not dylib) — single binary, no runtime dependencies
  • Developer ID Application cert required (not Apple Distribution) for outside-App-Store distribution

Build Commands

# Build Rust engine (from app/)
./scripts/build-rust.sh

# Open in Xcode
open app/R2Drop.xcworkspace

# Local Sparkle tooling checks (from app/)
cd app
make release-check-key
make release-verify-update-feed

# Install CLI locally
./scripts/install-cli.sh

# Package .dmg
./scripts/generate-dmg.sh

Known Issues & Gotchas

  • Apple cert .p12 export requires the private key to be in the same Keychain as the certificate. If you regenerate certs, use openssl genrsa + openssl req to create the CSR so you control the private key, then combine with openssl pkcs12 -export.
  • The .p12 password and APPLE_ID (email, not bundle ID) are both needed for notarization in CI.
  • Sparkle requires the Ed25519 public key in Info.plist under SUPublicEDKey.
  • CI release appcast signing is pinned to Sparkle CLI 2.9.0 in .github/workflows/release.yml.
  • Release appcast generation is fail-closed: missing Sparkle key or signature parse failure must fail the workflow.

Self-Update Instructions

When to update this file:

  • You discover a new pattern or convention in the codebase
  • You fix a bug whose root cause was non-obvious
  • You make an architectural decision or trade-off
  • You add a new dependency, script, or workflow
  • You learn something about the build process, CI, or Apple signing
  • A section below becomes stale or incorrect

How to update:

  1. Read this file at the start of each session
  2. As you work, note anything that would help future sessions
  3. Before ending, append or edit relevant sections
  4. Keep entries concise — this is a reference, not a journal

Session Log

2026-03-02

  • Release workflow hardened for Sparkle updates: pinned Sparkle CLI to 2.9.0, enforced required Sparkle private key (SPARKLE_ED25519_KEY preferred, SPARKLE_PRIVATE_KEY legacy), and fail-closed appcast signing.
  • Fixed root release workflow project path to app/R2Drop/R2Drop.xcodeproj.
  • Added local release-signing maintenance targets in app/Makefile (release-tools, release-public-key, release-check-key, release-sign-dmg, release-verify-update-feed) with sparkle-* aliases preserved.
  • Updated local release docs (app/RELEASE.md, app/README.md) to include Sparkle key validation and signed appcast verification steps.

2026-02-27

  • Full documentation audit against codebase. Fixed inaccuracies in gitbook docs, README.md, and CLAUDE.md.
  • Key fixes: exclusion patterns list was wrong (.Trashes not __Trashes, removed .env not in defaults), company name corrected to "Superhuman Intelligence LLC", CLI is macOS-only (not Linux/Windows), Homebrew tap marked as "coming soon" (not yet published), app installs CLI to /usr/local/bin (not ~/.local/bin), added missing config fields (exclusion_patterns, allow_anonymous_telemetry) to reference docs, fixed telemetry from "opt-in" to "on by default, opt-out", fixed email domain from r2drop.app to r2drop.com, added S3 credential derivation (SHA-256) to architecture docs, fixed automation script JSON field names.

2026-02-23

  • Initial CLAUDE.md created
  • Resolved Apple cert .p12 export issue: private key wasn't in Keychain because CSR was generated elsewhere. Fixed by generating key with openssl genrsa, creating CSR with openssl req, getting new cert from Apple, and combining with openssl pkcs12 -export.
  • All 5 GitHub Actions secrets now pushed: APPLE_CERTIFICATE_BASE64, APPLE_CERTIFICATE_PASSWORD, APPLE_TEAM_ID, APPLE_ID, APPLE_APP_SPECIFIC_PASSWORD
  • Fixed APPLE_ID from bundle ID to Apple ID email for notarization