Skip to main content

bucketwarden_server/
object_layout_support.rs

1use super::*;
2
3pub const OBJECT_LAYOUT_WHOLE_OBJECT: &str = "whole-object";
4pub const OBJECT_LAYOUT_CHUNKED: &str = "chunked";
5pub const OBJECT_LAYOUT_STRIPED: &str = "striped";
6pub const OBJECT_LAYOUT_PACKED_SMALL_OBJECTS: &str = "packed-small-objects";
7pub const OBJECT_LAYOUT_APPEND_ONLY_SEGMENTS: &str = "append-only-segments";
8
9const OBJECT_LAYOUT_CAPABILITIES: &[&str] = &[
10    "whole-object-layout",
11    "chunked-layout",
12    "striped-layout",
13    "packed-small-objects",
14    "append-only-segments",
15    "native-support-state",
16    "semantic-parity",
17    "configuration-admin-surface",
18    "security-governance-impact",
19    "observability-evidence",
20    "failure-mode-behavior",
21    "validation-test-coverage",
22    "product-specific-caveats",
23];
24
25const OBJECT_LAYOUT_CAVEATS: &[&str] = &[
26    "BucketWarden supports whole-object committed versions and multipart chunk assembly for the local runtime boundary.",
27    "Chunked layout proof is the S3 multipart path: ordered parts are assembled atomically into one committed object version.",
28    "Striped layout, packed small-object containers, and append-only segment stores are tracked but fail closed until they have runtime semantics.",
29    "Object layout proof does not claim distributed striping, small-object packing compaction, or log-segment garbage collection.",
30];
31
32const OBJECT_LAYOUT_FAILURE_MODES: &[&str] = &[
33    "unsupported-object-layout-rejected",
34    "invalid-chunk-size-rejected",
35    "striped-layout-policy-rejected",
36    "packed-small-object-policy-rejected",
37    "append-only-segment-policy-rejected",
38];
39
40#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
41pub struct ObjectLayoutSupportEntry {
42    pub layout: &'static str,
43    pub native_support: bool,
44    pub semantic_parity: &'static str,
45    pub write_path: &'static str,
46    pub read_path: &'static str,
47    pub failure_mode: &'static str,
48    pub caveat: &'static str,
49}
50
51#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
52pub struct ObjectLayoutSupportReport {
53    pub active_layout: &'static str,
54    pub supported_layouts: Vec<&'static str>,
55    pub unsupported_layouts: Vec<&'static str>,
56    pub default_chunk_policy: &'static str,
57    pub capabilities: Vec<&'static str>,
58    pub failure_modes: Vec<&'static str>,
59    pub caveats: Vec<&'static str>,
60    pub entries: Vec<ObjectLayoutSupportEntry>,
61}
62
63#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize)]
64pub struct ObjectLayoutPolicy {
65    pub layout: String,
66    pub chunk_size_bytes: Option<usize>,
67    pub stripe_width: Option<u8>,
68    pub pack_small_objects: bool,
69    pub append_only_segments: bool,
70}
71
72impl BucketWarden {
73    pub fn object_layout_support_report(&self) -> ObjectLayoutSupportReport {
74        ObjectLayoutSupportReport {
75            active_layout: OBJECT_LAYOUT_WHOLE_OBJECT,
76            supported_layouts: vec![OBJECT_LAYOUT_WHOLE_OBJECT, OBJECT_LAYOUT_CHUNKED],
77            unsupported_layouts: vec![
78                OBJECT_LAYOUT_STRIPED,
79                OBJECT_LAYOUT_PACKED_SMALL_OBJECTS,
80                OBJECT_LAYOUT_APPEND_ONLY_SEGMENTS,
81            ],
82            default_chunk_policy: "multipart-ordered-part-assembly",
83            capabilities: OBJECT_LAYOUT_CAPABILITIES.to_vec(),
84            failure_modes: OBJECT_LAYOUT_FAILURE_MODES.to_vec(),
85            caveats: OBJECT_LAYOUT_CAVEATS.to_vec(),
86            entries: vec![
87                ObjectLayoutSupportEntry {
88                    layout: OBJECT_LAYOUT_WHOLE_OBJECT,
89                    native_support: true,
90                    semantic_parity: "Single PUT writes one committed object version with body, metadata, lock, and encryption envelope.",
91                    write_path: "put_object commits the full object body atomically as one visible version.",
92                    read_path: "get_object and range reads return the committed whole-object bytes.",
93                    failure_mode: "Invalid object keys, digest mismatches, and lock conflicts reject before commit.",
94                    caveat: "Whole-object layout is local runtime behavior and does not claim remote block placement.",
95                },
96                ObjectLayoutSupportEntry {
97                    layout: OBJECT_LAYOUT_CHUNKED,
98                    native_support: true,
99                    semantic_parity: "Multipart uploads preserve ordered chunks and commit them into one visible object version.",
100                    write_path: "create_multipart_upload, upload_part, and complete_multipart_upload assemble ordered parts.",
101                    read_path: "reads observe the assembled object body after multipart completion.",
102                    failure_mode: "Missing parts, invalid part numbers, and ETag mismatches reject completion.",
103                    caveat: "Chunked support is S3 multipart assembly, not distributed chunk placement.",
104                },
105                ObjectLayoutSupportEntry {
106                    layout: OBJECT_LAYOUT_STRIPED,
107                    native_support: false,
108                    semantic_parity: "No cross-disk or cross-node stripe placement semantics are claimed.",
109                    write_path: "striped writes are out of the current runtime boundary.",
110                    read_path: "striped reads are out of the current runtime boundary.",
111                    failure_mode: "Striped layout selection is rejected as unsupported.",
112                    caveat: "Striping needs explicit shard placement and reconstruction contracts.",
113                },
114                ObjectLayoutSupportEntry {
115                    layout: OBJECT_LAYOUT_PACKED_SMALL_OBJECTS,
116                    native_support: false,
117                    semantic_parity: "No packed container, offset index, or compaction semantics are claimed.",
118                    write_path: "packed small-object writes are out of the current runtime boundary.",
119                    read_path: "packed small-object reads are out of the current runtime boundary.",
120                    failure_mode: "Packed small-object layout selection is rejected as unsupported.",
121                    caveat: "Packed small-object support needs container index and compaction proof.",
122                },
123                ObjectLayoutSupportEntry {
124                    layout: OBJECT_LAYOUT_APPEND_ONLY_SEGMENTS,
125                    native_support: false,
126                    semantic_parity: "No append-only object segment log or segment cleaner semantics are claimed.",
127                    write_path: "append-only segment writes are out of the current runtime boundary.",
128                    read_path: "append-only segment reads are out of the current runtime boundary.",
129                    failure_mode: "Append-only segment layout selection is rejected as unsupported.",
130                    caveat: "Append-only segment support needs segment index and recovery proof.",
131                },
132            ],
133        }
134    }
135
136    pub fn ensure_object_layout_supported(&self, layout: &str) -> Result<(), RuntimeError> {
137        let report = self.object_layout_support_report();
138        if report.supported_layouts.contains(&layout) {
139            Ok(())
140        } else {
141            Err(RuntimeError::UnsupportedObjectLayout(layout.to_string()))
142        }
143    }
144
145    pub fn validate_object_layout_policy(
146        &self,
147        policy: &ObjectLayoutPolicy,
148    ) -> Result<(), RuntimeError> {
149        self.ensure_object_layout_supported(&policy.layout)?;
150        if policy.stripe_width.is_some_and(|width| width > 1) {
151            return Err(RuntimeError::InvalidObjectLayoutPolicy(
152                "striped object layout is outside the current layout boundary".to_string(),
153            ));
154        }
155        if policy.pack_small_objects {
156            return Err(RuntimeError::InvalidObjectLayoutPolicy(
157                "packed small-object layout is outside the current layout boundary".to_string(),
158            ));
159        }
160        if policy.append_only_segments {
161            return Err(RuntimeError::InvalidObjectLayoutPolicy(
162                "append-only segment layout is outside the current layout boundary".to_string(),
163            ));
164        }
165        if matches!(policy.layout.as_str(), OBJECT_LAYOUT_CHUNKED)
166            && policy.chunk_size_bytes.is_some_and(|bytes| bytes == 0)
167        {
168            return Err(RuntimeError::InvalidObjectLayoutPolicy(
169                "chunked object layout requires a nonzero chunk size".to_string(),
170            ));
171        }
172        Ok(())
173    }
174}