Skip to content

feat: expose delete bucket - #229

Open
jzunigax2 wants to merge 5 commits into
masterfrom
feat/expose-delete-bucket
Open

jzunigax2 wants to merge 5 commits into
masterfrom
feat/expose-delete-bucket

Conversation

@jzunigax2

@jzunigax2 jzunigax2 commented Aug 24, 2026 •

Copy link
Copy Markdown

What

Adds DELETE /v2/gateway/users/:uuid/buckets/:id?name=<bucket-name>. removes a bucket, its entries, and credits the released bytes back to the user's totalUsedSpaceBytes in one call. Returns UserSpaceSnapshot (200).

Refuses (409) any bucket holding shard-backed entries.

200 bucket, entries and bytes released; returns the snapshot
400 malformed uuid / object id, or missing name
403 bucket belongs to another user
404 unknown user, unknown bucket, or name mismatch
409 bucket holds shard-backed entries. refused, nothing deleted

Why

mail-server is building the purge of suspended mail accounts: a downgraded plan suspends the account today, and 30 days later it has to be destroyed for real —the Stalwart principal, the local tables, and the Bridge storage behind it. Mail
already promises the user a deletionAt date it can't yet act on.

Each mail address owns one Bridge bucket, and Bridge is the only place that can release its entries and the quota they hold. There was no gateway route to do that, so the purge had nothing to call — BridgeClient.deleteMailBucket() on the
mail side was a placeholder pointing at a path Bridge never served. This is the Bridge half of that task; the mail-server client is now moved onto the real route.

The wholesale delete is safe because gateway-minted entries (createEntry) are metadata only: a bucket, a size, a version — nothing downstream. Real uploads always set frame (v1) or index + hmac (v2). That is the discriminator used
throughout, and dropping a shard-backed entry wholesale would strand its shards, mirrors and farmer bytes with nothing left pointing at them — hence the 409.

How

Order of operations:

  1. Load user + bucket. 404 unknown user, 404 unknown bucket, 403 wrong owner, 404 if name doesn't match.
  2. hasShardBackedEntriesByBucket: countDocuments with limit: 1, O(1) refusal before any scan.
  3. summarizeByBucket: one aggregate returning shardBackedCount and metadataOnlyBytes; refuses again if the count is non-zero.
  4. deleteMetadataOnlyByBucket: the delete filter itself excludes shard-backed entries, so a mixed bucket cannot strand anything even if every check above were wrong.
  5. Single negative $inc on totalUsedSpaceBytes, only when bytes > 0.
  6. hasEntriesByBucket: anything left means something was shard-backed: 409, and the bucket document is kept.
  7. Remove the bucket document.

…dd error handling for shard-backed buckets in the use case. Update HTTP gateway to support bucket deletion endpoint. Enhance tests for bucket deletion scenarios.
…ted repository interface. Simplify bucket entry removal logic in the use case and enhance test stubs for better clarity and maintainability.
@jzunigax2
jzunigax2 requested a review from sg-gs as a code owner August 24, 2026 06:21
@jzunigax2
jzunigax2 marked this pull request as draft August 24, 2026 06:22
@jzunigax2
jzunigax2 requested a review from apsantiso August 24, 2026 06:22
Comment thread lib/core/bucketEntries/usecase.ts Outdated
Comment on lines +288 to +292
const [user, bucket, sample] = await Promise.all([
this.usersRepository.findByUuid(userUuid),
this.bucketsRepository.findOne({ id: bucketId }),
this.bucketEntriesRepository.findOne({ bucket: bucketId }),
]);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How are you going to make sure the bucket is the mail one? is there any way you can guarantee that only mail buckets are removed?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's one of my worries, currently there is a guard that checks for one bucket entry and then checks if it has shards associated. since mail generates none it assumes it is a mail bucket but it is a bit of a fragile check

…cking shard-backed and metadata-only entries. Update repository interface and adjust bucket entry deletion logic in the use case to include bucket name validation. Improve HTTP gateway controller to require bucket name on deletion requests. Update tests to cover new functionality and edge cases.
…summarizing bucket entries and deleting metadata-only entries. Update the repository interface accordingly. Enhance bucket entry use case to utilize new summary method and improve logging during bucket deletion. Update tests to reflect changes in repository methods and ensure proper functionality.
@jzunigax2
jzunigax2 requested a review from apsantiso August 27, 2026 23:59
@jzunigax2
jzunigax2 marked this pull request as ready for review August 27, 2026 23:59
@sg-gs

sg-gs commented Aug 31, 2026

Copy link
Copy Markdown
Member

Why do you need this @jzunigax2 ? Please provide a proper description

@jzunigax2

Copy link
Copy Markdown
Author

description

ups sorry @sg-gs , I simply undrafted this. Updated description @sg-gs this is the bride side part of the mail account auto delete task. Involves cleaning up mail created metadata

Comment thread lib/core/bucketEntries/MongoDBBucketEntriesRepository.ts
Comment thread lib/core/bucketEntries/MongoDBBucketEntriesRepository.ts
…yBucket in MongoDBBucketEntriesRepository. Update repository interface and adjust BucketEntriesUsecase to utilize the new method. Modify tests to reflect these changes and ensure proper functionality.
@jzunigax2
jzunigax2 requested a review from sg-gs September 3, 2026 22:28
@apsantiso

apsantiso commented Sep 8, 2026 •

Copy link
Copy Markdown
Member

@jzunigax2 Can you add some small comment in the controller so it's cleaner that the EP only deletes entries created from the gateway?

Just so we can remember this later, it's clear if you read the PR but this repo is somehow hard to navigate through so when it gets merged you'll need to dive into the funcions to be able to know what's happening

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants