QMail Object Delete — Group 6, Code 84

Owner-authorized atomic deletion of a committed object. Publishes a tombstone generation rather than removing history in place, keeping the object ID stable and the operation idempotent.

Quick reference

Command Group6
Command Code84
Server functioncmd_qmail_object_delete in qmail_object_transfer.c
Wire structuresqmail_object_delete_req_t, qmail_object_delete_resp_t
Body layoutPreamble (48) + Common prefix (16) + Delete request (40) = 104 bytes request header
TransportTCP only
EncryptionRequired — AES-128
AccessOwner only
IdempotentYes, for the same owner and generation pair

Purpose

Only the authenticated owner — the identity that created the object, tracked since object_begin — may delete it. A successful delete atomically publishes a tombstone at the requested target generation, making the object unavailable to new object_info and object_get_range requests from that point on, and returns that tombstone generation in the response. Existing range responses already in progress at the moment of deletion may finish normally against the generation they were pinned to.

Deletion does not imply a payment refund — the storage payment made at begin time is not returned. Physical bytes may be reclaimed only after any active readers release the old generation and the configured deletion grace period expires; the tombstone itself is retained so repeated delete calls remain idempotent.

Request body

Preamble (48 bytes, offsets 0–47)

Standard QMail preamble. See QMail Overview — Universal preamble.

Common prefix (16 bytes, offsets 48–63)

See object_begin for the shared common-prefix field table. command_header_length must equal 40 for this command.

Delete request payload (40 bytes, offsets 64–103)

object_id (16 bytes) 64 79 FT 80 reserved (7) 81 87 expected_generation (8, BE) 88 95 target_generation (8, BE) 96 103
OffsetSizeFieldDescription
64–7916object_idThe stable object identifier.
801file_typeSame encoding as cmd 70/74: 0x00 meta, 0x01 body, 0x0A+ attachments.
81–877reservedMust be zero.
88–958expected_generationUnsigned big-endian. Must exactly match the current live generation (compare-and-swap semantics).
96–1038target_generationUnsigned big-endian. Must be strictly greater than expected_generation. Becomes the tombstone's generation number.
last 22TerminatorFixed clear-text terminator.

Response body

Successful response header length: 64 bytes (16-byte common prefix + 48-byte delete response). No data follows.

FieldSizeDescription
common_prefix16Echoes request_id.
object_id16Echo of the requested object_id.
file_type1Echo of the requested file_type.
object_state12 = tombstone.
reserved6Zero.
tombstone_generation8The generation just published as the tombstone — equal to the request's target_generation.
deleted_at8Unix seconds when the tombstone was published.
reserved8Zero.

Status codes

CodeSymbolMeaning
250SUCCESSTombstone published; see response body.
202ERROR_FILE_NOT_EXISTNo committed object exists at this object_id/file_type, or it is already a tombstone.
218ERROR_TCP_REQUIREDRetry using TCP.
219ERROR_UNSUPPORTED_PROTOCOLUnsupported protocol_version or framing version.
232ERROR_NOT_OBJECT_OWNERThe authenticated caller may read the object under the Phase I allow-all ACL but is not its owner, so it may not delete it.
233ERROR_GENERATION_CONFLICTexpected_generation does not match the current live generation, or target_generation is not strictly greater than expected_generation.

Idempotency

Deletion is idempotent when the same owner repeats the call with the same expected_generation and target_generation — the server recognizes the tombstone already matches and returns the original successful result rather than rejecting the retry. A retry that names a different target_generation than the tombstone actually published is treated as a fresh delete attempt against the now-current (tombstoned) generation and is rejected with ERROR_GENERATION_CONFLICT, since expected_generation no longer matches.

Common mistakes

Expecting a payment refund

Deleting an object does not reverse the storage payment made when it was created. Phase I's payment model charges once per (owner, object_id, locker_code) and does not track or settle a byte-accurate refund on deletion.

Assuming bytes are gone immediately

A successful delete makes the object logically unavailable to new reads immediately, but physical storage reclamation only happens after active readers release the old generation and the configured deletion grace period (qmail_delete_grace_seconds) elapses. Do not build capacity-planning logic that assumes space is freed synchronously.