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}