server/docs/collections/collections-architecture.md
This document is the canonical overview for Ente's collections architecture. It explains shared rules around add/move/remove, required encryption envelopes, and how server APIs enforce behavior across mobile and server clients.
(encryptedKey, keyDecryptionNonce) alongside the membership.POST /files/trash.When a user removes their own files from a collection they own:
apps/locker/lib/services/collections/collections_service.dart:428, apps/locker/lib/services/collections/collections_service.dart:583.apps/photos/lib/ui/actions/collection/collection_sharing_actions.dart:578, apps/photos/lib/ui/actions/collection/collection_sharing_actions.dart:720.POST /collections/add-files
role.canAdd and ownership of every file ID.POST /collections/move-files
collectionOwner == fileOwner before updating memberships (add to target, mark deleted in source).POST /collections/v3/remove-files
collection_files.action='REMOVE' with action_user to the admin.pending_actions entry for the owner with action='REMOVE' and data={fileIDs: [...]}.POST /files/trash
GET /collections/v2/diff
action in {REMOVE,DELETE} and is_deleted=false:
isDeleted=true and omits action/actionUser.action and actionUser so the client can take follow-up action.action='DELETE_SUGGESTED'.GET /trash/v2/diff
apps/locker/lib/services/collections/collections_api_client.dart:270; Photos trash sync at apps/photos/lib/services/sync/trash_sync_service.dart:154).POST /collections/share.POST /collections/unshare.POST /collections/leave/{id}.POST /collections/share-url, PUT /collections/share-url, DELETE /collections/share-url/{id}.DELETE /collections/v3/{collectionID}
keepFiles=true, the collection must already be empty.keepFiles=false, trash the owner's files, remove other users' memberships, revoke share URLs, and schedule deletion.ente/server/cmd/museum/main.go.ente/server/pkg/controller/access/collection.go, ente/server/pkg/controller/access/file.go, roles in ente/server/ente/access.go.ente/server/pkg/controller/collections/file_action.go, ente/server/pkg/controller/collections/collection.go, ente/server/pkg/controller/collections/share.go.ente/server/pkg/repo/collection.go, membership helpers in ente/server/pkg/repo/collection_files.go.apps/locker/lib/services/collections/collections_api_client.dart, apps/locker/lib/services/collections/collections_service.dart.apps/photos/lib/services/collections_service.dart, apps/photos/lib/ui/actions/collection/collection_sharing_actions.dart, apps/photos/lib/services/sync/trash_sync_service.dart.packages/sharing/lib/collection_sharing_service.dart./collections/v2/diff, /trash/v2/diff) to drive sync loops and UI refreshes.
POST /collections/suggest-delete (owner or admin only). Suggests deleting files owned by others in the collection. The server sets action='DELETE_SUGGESTED' with action_user=actor and leaves is_deleted=false.
action='REMOVE' and create two pending_actions for the owner: one REMOVE, one DELETE_SUGGESTED.pending_actions entry for that user with action='DELETE_SUGGESTED'.collection_actions(id, user_id, actor_user_id, collection_id, file_id, data JSONB, action, is_pending, created_at, updated_at).GET /collection-actions/pending-remove?sinceTime=<int64> returns REMOVE actions for the authenticated owner so clients can guide the shared-collection workflow. Response: { "actions": [...], "hasMore": boolean }.GET /collection-actions/delete-suggestions?sinceTime=<int64> returns DELETE_SUGGESTED actions; the server double-checks ownership and marks as resolved any entries whose files already exist as non-restored trash rows (either still in trash or already permanently deleted). Response: { "actions": [...], "hasMore": boolean }.POST /collection-actions/reject-delete-suggestions accepts a list of file IDs and marks the matching pending delete suggestions as resolved for the caller.REMOVE by ensuring file remains in at least one owned collection, then can finalize server-side removal.DELETE_SUGGESTED prompts file owner to delete files locally if desired.