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 Group6
Command Code91
Server functioncmd_qmail_object_set_acl (proposed)
TransportTCP only
EncryptionRequired
AuthorisationObject owner, or an ACL holder with the setacl permission
SemanticsWhole-list REPLACE, not a merge
IdempotentYes — 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.

PermissionBitGoverns
read0x01object_get_range and object_info
write0x02Creating, replacing or committing a generation; extending retention
delete0x04object_delete
setacl0x08This command
Subject kindValueMatches
owner0The object's recorded owner coin.
any1Every caller, including unauthenticated readers.
coin2One 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+)

OffsetSizeFieldDescription
64–7916object_idThe committed object whose ACL is being replaced.
801file_typeSame file_type used at object_begin.
811ace_countNumber of ACEs that follow. 1–32. Zero is rejected — an object with no ACEs would be unreachable by anyone, including its owner.
82–876reservedMust be zero.
88+8 × ace_countacesThe ACE array, described below.
last 22TerminatorFixed clear-text terminator.

ACE entry (8 bytes each)

OffsetSizeFieldDescription
+01allow1 = allow, 0 = deny. Deny wins over any allow.
+11subject_kind0 = owner, 1 = any, 2 = coin.
+21denominationSigned denomination for subject_kind 2; zero otherwise.
+34serial_numberBig Endian serial number for subject_kind 2; zero otherwise.
+71permissionsBitwise OR of the permission bits above.

Response body

FieldSizeDescription
common_prefix16Echoes request_id.
object_id16Echo of the request's object_id.
acl_version4Big Endian. The version after this write. Increments on every accepted change.
ace_count1ACEs now stored.
reserved11Zero.

Status codes

CodeSymbolMeaning
250SUCCESSACL replaced.
218ERROR_TCP_REQUIREDRetry using TCP.
228ERROR_OBJECT_NOT_COMMITTEDNo committed object with this object_id and file_type.
166ERROR_ACL_DENIEDThe caller lacks the setacl permission on this object.
232ERROR_NOT_OBJECT_OWNERThe caller is not the owner and holds no ACL grant at all.
198ERROR_INVALID_PARAMETERMalformed 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.
163ERROR_VOLUME_UNAVAILABLEThe 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.