Skip to content

Add legacy native update bridge for 2.0.3–2.0.9 clients - #55

Merged
loadchange merged 1 commit into
mainfrom
claude/loving-turing-79c5ov
Sep 25, 2026
Merged

loadchange merged 1 commit into
mainfrom
claude/loving-turing-79c5ov

Conversation

@loadchange

Copy link
Copy Markdown
Collaborator

Summary

Implements a legacy native update bridge to enable CCbuddy 2.0.3–2.0.9 (native Swift) and Tauri 1.3.9 clients to update to the new Electron-based releases. These frozen clients only read releases/latest/download/latest.json with a hardcoded minisign public key, but Electron releases only publish latest-mac.yml. This change generates the required latest.json manifest and re-signs the app under the legacy identity so old clients can install and update.

Key Changes

  • legacy-native-update.mjs: Core utility script that:

    • Parses and validates minisign public keys and Tauri signatures
    • Verifies archive signatures against the legacy updater's embedded public key (FB130F2908B61575)
    • Generates latest.json manifests with arm64-only platform keys (Intel clients fall back to manual download)
    • Validates symlinks don't escape the app bundle (old client extractor requirement)
    • Provides write, verify, and check-symlinks commands
  • package-legacy-native-bridge.sh: macOS release workflow script that:

    • Copies the signed production app and re-signs it with legacy bundle ID (dev.ccbud.gateway)
    • Modifies the designated requirement to team-only, allowing the bridge to accept subsequent app.ccbuddy.desktop updates
    • Notarizes and staples the bridge app
    • Creates a gzip tar archive with single .app root (old client requirement)
    • Signs the archive with Tauri CLI using the legacy updater key
    • Generates and verifies latest.json before publication
  • legacy-native-update.test.mjs: Comprehensive test suite covering:

    • Minisign key parsing and formatting
    • Signature verification with key ID validation
    • Archive tampering detection
    • Base64 canonicality enforcement (Swift compatibility)
    • Manifest drift detection
    • Symlink escape detection
  • Release workflow integration (.github/workflows/release.yml):

    • Adds bridge packaging step after notarization
    • Uploads bridge artifacts (latest.json, .tar.gz, .sig) alongside normal Electron assets
    • Passes Tauri signing credentials securely
  • Documentation (docs/specs/legacy-native-update-bridge.md):

    • Specifies the frozen client contract (manifest format, signature scheme, code signing requirements)
    • Documents the bridge identity handover mechanism
    • Defines architecture scope (arm64 only) and ownership model

Notable Implementation Details

  • The bridge uses a team-only designated requirement (anchor apple generic and certificate leaf[subject.OU] = "2CGR266XD2") so Squirrel.Mac accepts the next normal app.ccbuddy.desktop update
  • Signature verification uses the same rules as the old client's MinisignUpdateVerifier: BLAKE2b-512 prehashing with separate global signature verification
  • Base64 validation is strict (canonical form only) to match Swift's Data(base64Encoded:) behavior
  • Archive validation enforces single .app root, no absolute paths, no .., and no symlinks escaping the bundle
  • The embedded public key is immutable; any Team ID change would break all old clients

https://claude.ai/code/session_017zzyG8j5ALC6AdRu24Zp3c

The native Swift 2.0.x and Tauri 1.3.9 updaters are frozen and only read
releases/latest/download/latest.json. Electron releases publish just
latest-mac.yml, so those clients got a 404 and could not reach 2.0.12.

Each release now also ships latest.json, a minisign-signed
CCbuddy-<version>-legacy-mac-arm64.app.tar.gz and its .sig. The archive
holds the signed Electron app re-signed as dev.ccbud.gateway (the identity
the old client requires) with a team-only designated requirement, so the
bridge can later accept the normal app.ccbuddy.desktop update through
Squirrel. The bridge is notarized and stapled, and latest.json is
re-verified against the legacy public key before and after upload.
Only arm64 keys are listed; Intel clients fall back to manual download.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017zzyG8j5ALC6AdRu24Zp3c
@loadchange
loadchange merged commit 3c08594 into main Sep 25, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants