Skip to content

feat(collab): add encrypted asset transfer - #13

Merged
EricTsai83 merged 1 commit into
mainfrom
feat/collab-encrypted-asset-transfer
Aug 5, 2026
Merged

feat(collab): add encrypted asset transfer#13
EricTsai83 merged 1 commit into
mainfrom
feat/collab-encrypted-asset-transfer

Conversation

@EricTsai83

@EricTsai83 EricTsai83 commented Aug 5, 2026

Copy link
Copy Markdown
Owner

Room images now travel as ciphertext. The browser seals every asset under an HKDF-derived key (purpose asset, bound to room, authorization generation and the Excalidraw file id) before upload, so the app backend and the object store only ever hold bytes neither can read. Elements keep going through the relay unchanged; the two paths are aligned solely by the Plan 16 identity pair, which is why no new realtime message type was needed.

  • @drawstuff/collaboration/asset: versioned payload framing (data URL carried verbatim rather than JSON-escaped), MIME allowlist, byte budgets and AssetCryptoCodec; replaces Plan 16's manifest schema
  • collaboration_asset gains crypto_version, ut_file_key, url, byte_length (non-destructive db:push; the table held 0 rows)
  • collaborationAsset.resolve replaces list/register; assets enter a room only through a new uploadthing route that re-checks room access, role and generation inside the room lock, because an upload takes as long as its bytes
  • client asset store: store-wide transfer budget, batched lookups, deduplicated downloads, bounded body reads, backoff retry for "not uploaded yet" and permanent abandon for bytes that cannot be opened
  • retiring a generation enqueues its storage keys for the cleanup worker in the same transaction that deletes the rows, since object storage cannot join it

Two Codex GPT-5.6 Sol review passes produced 12 findings: 9 accepted, 1 partially accepted, 2 rejected. Content-hash verification was rejected on evidence — an Excalidraw file id is the digest of the file the user picked while the stored data URL is the engine's resized re-encoding, so 41 of this project's 67 real assets would have been refused. The deriveRoomKey/REALTIME_CRYPTO_VERSION coupling was rejected as Plan 14 behaviour shared identically by snapshots; it and room-scoped retention are now owned by Plan 19.

Summary by CodeRabbit

  • New Features

    • Added encrypted collaboration asset synchronization for images and other binary files.
    • Assets now upload, download, retry, and render automatically for collaborators and late joiners.
    • Added secure asset validation, size limits, deduplication, and bounded transfer handling.
    • Updated collaboration sessions to publish, request, and restore referenced files.
  • Bug Fixes

    • Improved handling of failed, missing, corrupted, duplicate, and outdated assets with cleanup and cancellation support.
  • Documentation

    • Updated persistence documentation and marked encrypted asset transfer as complete.

Room images now travel as ciphertext. The browser seals every asset under an
HKDF-derived key (purpose `asset`, bound to room, authorization generation and
the Excalidraw file id) before upload, so the app backend and the object store
only ever hold bytes neither can read. Elements keep going through the relay
unchanged; the two paths are aligned solely by the Plan 16 identity pair, which
is why no new realtime message type was needed.

- `@drawstuff/collaboration/asset`: versioned payload framing (data URL carried
  verbatim rather than JSON-escaped), MIME allowlist, byte budgets and
  `AssetCryptoCodec`; replaces Plan 16's manifest schema
- `collaboration_asset` gains crypto_version, ut_file_key, url, byte_length
  (non-destructive `db:push`; the table held 0 rows)
- `collaborationAsset.resolve` replaces `list`/`register`; assets enter a room
  only through a new uploadthing route that re-checks room access, role and
  generation inside the room lock, because an upload takes as long as its bytes
- client asset store: store-wide transfer budget, batched lookups, deduplicated
  downloads, bounded body reads, backoff retry for "not uploaded yet" and
  permanent abandon for bytes that cannot be opened
- retiring a generation enqueues its storage keys for the cleanup worker in the
  same transaction that deletes the rows, since object storage cannot join it

Two Codex GPT-5.6 Sol review passes produced 12 findings: 9 accepted, 1 partially
accepted, 2 rejected. Content-hash verification was rejected on evidence — an
Excalidraw file id is the digest of the file the user picked while the stored
data URL is the engine's resized re-encoding, so 41 of this project's 67 real
assets would have been refused. The `deriveRoomKey`/`REALTIME_CRYPTO_VERSION`
coupling was rejected as Plan 14 behaviour shared identically by snapshots; it
and room-scoped retention are now owned by Plan 19.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Aug 5, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
drawstuff Error Error Aug 5, 2026 10:57am

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 745507cd-afab-474a-8662-48f6c1447d0a

📥 Commits

Reviewing files that changed from the base of the PR and between 9383292 and 351d375.

📒 Files selected for processing (26)
  • apps/web/src/app/api/uploadthing/core.ts
  • apps/web/src/hooks/excalidraw/use-collaboration-room.ts
  • apps/web/src/lib/collab/asset-store.ts
  • apps/web/src/lib/collab/asset-upload.ts
  • apps/web/src/lib/collab/collaboration-session.ts
  • apps/web/src/lib/collab/room-session.ts
  • apps/web/src/server/api/routers/collaboration-asset.ts
  • apps/web/src/server/collab/assets.ts
  • apps/web/src/server/db/schema.ts
  • apps/web/tests/collab-asset-transfer.test.ts
  • apps/web/tests/collaboration-asset-identity.test.ts
  • apps/web/tests/support/collab-scene-fixtures.ts
  • apps/web/tests/support/collab-session-harness.ts
  • docs/adr/0001-excalidraw-persistence-boundary.md
  • packages/collaboration/src/asset.ts
  • packages/collaboration/tests/asset.test.ts
  • packages/collaboration/tests/package-contract.test.ts
  • packages/collaboration/vitest.config.ts
  • packages/excalidraw-adapter/src/codec.ts
  • packages/excalidraw-adapter/src/persistence-contract.ts
  • packages/excalidraw-adapter/tests/package-contract.test.ts
  • packages/excalidraw-adapter/tests/persistence-contract.test.ts
  • plans/17-encrypted-asset-transfer.md
  • plans/19-production-hardening.md
  • plans/23-owned-scene-asset-lifecycle.md
  • plans/README.md

📝 Walkthrough

Walkthrough

The PR adds encrypted binary asset synchronization for collaboration rooms. It introduces room-scoped encryption, bounded transfer state, storage-backed uploads, requested-asset resolution, session integration, cleanup handling, and comprehensive tests.

Changes

Encrypted collaboration asset transfer

Layer / File(s) Summary
Asset crypto and reference contracts
packages/collaboration/src/asset.ts, packages/excalidraw-adapter/src/*, packages/collaboration/tests/asset.test.ts
Adds versioned payload encoding, AES-GCM sealing, room-generation key derivation, lookup schemas, and deduplicated referenced file ID collection.
Asset storage and upload API
apps/web/src/server/db/schema.ts, apps/web/src/server/collab/assets.ts, apps/web/src/server/api/routers/collaboration-asset.ts, apps/web/src/app/api/uploadthing/core.ts, apps/web/src/lib/collab/asset-upload.ts
Stores encrypted asset metadata, resolves requested IDs, validates authorized uploads, records assets transactionally, and queues retired storage objects for cleanup.
Asset store and room integration
apps/web/src/lib/collab/asset-store.ts, apps/web/src/lib/collab/collaboration-session.ts, apps/web/src/lib/collab/room-session.ts, apps/web/src/hooks/excalidraw/use-collaboration-room.ts
Adds bounded, deduplicated uploads and downloads with retries, then connects them to scene publication, remote file injection, room authorization, and teardown.
Asset synchronization test harness
apps/web/tests/support/collab-session-harness.ts, apps/web/tests/support/collab-scene-fixtures.ts
Adds binary file fixtures, an in-memory asset backend, controllable failures and delays, manual retry timers, and asset-aware clients.
Transfer and persistence validation
apps/web/tests/collab-asset-transfer.test.ts, apps/web/tests/collaboration-asset-identity.test.ts, docs/adr/0001-excalidraw-persistence-boundary.md, plans/*
Validates encrypted transfer, authorization, retries, deduplication, limits, cleanup, teardown, generation rotation, and the documented lifecycle decisions.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant CollaborationSession
  participant AssetStore
  participant AssetAPI
  participant ObjectStorage

  User->>CollaborationSession: add image to scene
  CollaborationSession->>AssetStore: publish referenced local asset
  AssetStore->>AssetAPI: upload encrypted ciphertext
  AssetAPI->>ObjectStorage: store ciphertext
  CollaborationSession->>AssetStore: request missing asset IDs
  AssetStore->>AssetAPI: resolve asset records
  AssetStore->>ObjectStorage: download ciphertext
  AssetStore->>CollaborationSession: inject decrypted file
Loading

Possibly related PRs

  • EricTsai83/drawstuff#8: Both changes update asset upload completion validation and failure or cleanup handling.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/collab-encrypted-asset-transfer

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@EricTsai83
EricTsai83 merged commit 3564cac into main Aug 5, 2026
1 of 5 checks passed
@EricTsai83
EricTsai83 deleted the feat/collab-encrypted-asset-transfer branch August 5, 2026 10:57
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.

1 participant