1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
//! Sealing the payload fields a record carries, without hiding the record.
//!
//! # Why a field and not the whole record
//!
//! Wrapping a whole [`RecordKind`](super::RecordKind) in a sealed variant is the obvious design
//! and it is wrong here, for a reason that compiles and passes tests: both
//! store backends match on the concrete variant. redb keys the **exactly-once**
//! index off `EffectStarted`, and both backends key the outcome index off
//! `RunSealed`. A record whose variant became a sealed wrapper would still
//! build, still pass every test that writes unsealed records, and silently stop
//! enforcing exactly-once — the guarantee whose failure is a payment taken
//! twice.
//!
//! So the variant stays exactly what it was and only the *payload* is sealed —
//! every field that carries the caller's data, enumerated in `payloads`
//! (crate-private: the list is a rule this crate applies, not a surface a
//! caller selects from).
//! Everything the runtime routes on (`seq`, `run`, `case`, `step`, `phase`,
//! `epoch`, `effect_key`, and the variant itself) stays in the clear, so
//! exactly-once, the case scan, the outcome index and the chain all keep
//! working with no key at all.
//!
//! # The chain commits to ciphertext
//!
//! A sealed payload is an ordinary JSON value, so the record serialises and
//! hashes exactly as it always did — over the sealed bytes. That is the
//! decision worth stating, because the alternative is tempting and worse:
//! hashing the plaintext would tie tamper evidence to the key, and destroying
//! the key would erase both the data *and* the ability to prove nothing had
//! been altered. Committing to ciphertext means an auditor with **no keys**
//! still verifies the chain of a run whose payloads are gone — the same shape
//! blobs already have, where the chain commits to a digest and the bytes stay
//! erasable.
use ;
/// The reserved key marking a sealed payload.
///
/// A payload that legitimately contained this key as its *only* key would be
/// indistinguishable from a sealed one, so the name is deliberately not
/// something a business document would carry, and the shape is checked
/// exactly: one key, whose value is a string.
pub const SEALED: &str = "$sealed";
/// Whether this value is a sealed payload rather than a readable one.
/// Whether this string field is a sealed payload rather than readable text.
///
/// The string counterpart of [`is_sealed`], for the fields whose schema is a
/// string rather than a value — a note's text, a failure's message. The same
/// caveat applies in the same shape: text that legitimately began with the
/// marker and decoded as base64 to its end would be indistinguishable, so the
/// marker is deliberately not something prose would open with.
/// Wrap an envelope as the JSON a record carries in place of its payload.
pub
/// The envelope inside a sealed payload, if this is one.
pub
/// Wrap an envelope as the string a record carries in place of a text field.
pub
/// The envelope inside a sealed text field, if this is one.
pub
/// One sealable field of a record, by the shape its schema gives it.
///
/// Two arms rather than coercing text into a JSON value, because the record's
/// wire format is the field's declared type: a `Note`'s `text` is a string on
/// the wire, and sealing must replace it with a string or every reader of the
/// serialized record changes shape with the key configuration.
pub
/// The payload fields of a record kind, for sealing and opening in place.
///
/// One list, consulted by both directions, because a field sealed on the way
/// in and forgotten on the way out is a record nobody can read — and the
/// reverse is a payload that was never sealed at all.
///
/// The dividing rule: *what a store is asked questions about stays readable;
/// what it merely holds is sealed.* Neither backend matches or
/// indexes on any field below — routing lives in `seq`, `run`, `case`,
/// `effect_key`, the variant, and `RunSealed.outcome`, all of which stay
/// clear.
pub