QMail Object Set ACL — Group 6, Code 91
Replaces the access-control list on a committed object, so an owner can share it with specific coins or make it private. Read it back with object_get_acl.
PROPOSED — NOT YET IMPLEMENTED
This page documents a command that does not exist on any RAIDA yet. Command code 91 is reserved in qmail_structs.h but has no protocol.c dispatch entry, so requests currently return ERROR_INVALID_COMMAND. Published for design review; do not build against it until this banner is removed.
Quick reference
| Command Group | 6 |
| Command Code | 91 |
| Server function | cmd_qmail_object_set_acl (proposed) |
| Transport | TCP only |
| Encryption | Required |
| Authorisation | Object owner, or an ACL holder with the setacl permission |
| Semantics | Whole-list REPLACE, not a merge |
| Idempotent | Yes — sending the same list twice is a no-op |
Purpose
Every object already carries a durable ACL, written when it is committed. The shipped default grants the owner full rights and grants world read, which is why objects behave as publicly readable today. Until now there has been no way to change that list — the permission engine and the on-disk format existed, but no command exposed them.
object_set_acl closes that gap. It replaces the entire access-control entry (ACE) list in one atomic write and bumps the object's acl_version.
Permission model
An ACL is an ordered list of ACEs. Each ACE names a subject, a set of permissions, and whether it allows or denies them. Deny takes precedence over allow: if any matching ACE denies a permission, the operation is refused regardless of other entries.
| Permission | Bit | Governs |
|---|---|---|
| read | 0x01 | object_get_range and object_info |
| write | 0x02 | Creating, replacing or committing a generation; extending retention |
| delete | 0x04 | object_delete |
| setacl | 0x08 | This command |
| Subject kind | Value | Matches |
|---|---|---|
| owner | 0 | The object's recorded owner coin. |
| any | 1 | Every caller, including unauthenticated readers. |
| coin | 2 | One specific coin, identified by denomination and serial number. |
Request body
Preamble (48 bytes) and common prefix (16 bytes)
As for the other object-transfer commands. The preamble coin authenticates the caller.
Set-ACL payload (variable, offsets 64+)
| Offset | Size | Field | Description |
|---|---|---|---|
| 64–79 | 16 | object_id | The committed object whose ACL is being replaced. |
| 80 | 1 | file_type | Same file_type used at object_begin. |
| 81 | 1 | ace_count | Number of ACEs that follow. 1–32. Zero is rejected — an object with no ACEs would be unreachable by anyone, including its owner. |
| 82–87 | 6 | reserved | Must be zero. |
| 88+ | 8 × ace_count | aces | The ACE array, described below. |
| last 2 | 2 | Terminator | Fixed clear-text terminator. |
ACE entry (8 bytes each)
| Offset | Size | Field | Description |
|---|---|---|---|
| +0 | 1 | allow | 1 = allow, 0 = deny. Deny wins over any allow. |
| +1 | 1 | subject_kind | 0 = owner, 1 = any, 2 = coin. |
| +2 | 1 | denomination | Signed denomination for subject_kind 2; zero otherwise. |
| +3 | 4 | serial_number | Big Endian serial number for subject_kind 2; zero otherwise. |
| +7 | 1 | permissions | Bitwise OR of the permission bits above. |
Response body
| Field | Size | Description |
|---|---|---|
| common_prefix | 16 | Echoes request_id. |
| object_id | 16 | Echo of the request's object_id. |
| acl_version | 4 | Big Endian. The version after this write. Increments on every accepted change. |
| ace_count | 1 | ACEs now stored. |
| reserved | 11 | Zero. |
Status codes
| Code | Symbol | Meaning |
|---|---|---|
| 250 | SUCCESS | ACL replaced. |
| 218 | ERROR_TCP_REQUIRED | Retry using TCP. |
| 228 | ERROR_OBJECT_NOT_COMMITTED | No committed object with this object_id and file_type. |
| 166 | ERROR_ACL_DENIED | The caller lacks the setacl permission on this object. |
| 232 | ERROR_NOT_OBJECT_OWNER | The caller is not the owner and holds no ACL grant at all. |
| 198 | ERROR_INVALID_PARAMETER | Malformed ACE list: ace_count is 0 or above 32, an unknown subject_kind or permission bit, a non-zero reserved field, or a body length that disagrees with ace_count. |
| 163 | ERROR_VOLUME_UNAVAILABLE | The volume holding this object is offline; the ACL cannot be written right now. |
Common mistakes
Locking yourself out
This is a REPLACE, not a merge. A list that omits any ACE granting the owner setacl leaves nobody able to change the ACL again. Servers reject ace_count = 0 for this reason, but they do not otherwise stop an owner from writing a list that excludes themselves. Always include an owner ACE with full rights unless you specifically intend a one-way change.
Expecting a private object to become unreadable everywhere at once
The ACL is per RAIDA, and each server stores only its own stripe. Tightening permissions on eight of nine servers still leaves the ninth serving its stripe under the old rules. Apply the change fleet-wide and check every response.
Assuming deny is the default
Objects committed before this command existed carry the shipped default of owner-full plus world-read. Removing world-read is an explicit act; it does not happen because you added a coin-specific allow.