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
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
//! Kitsune2 bootstrap server is an HTTP REST server for handling bootstrapping
//! discovery of peer network reachability in p2p applications.
//!
//! Despite being in the kitsune2 repo, `bootstrap_srv` and `bootstrap_cli`
//! do not depend on any Kitsune2 crates. This is to ensure the bootstrapping
//! functionality is well-defined, self-contained, easily testable in
//! isolation, and usable for projects that don't choose to make use of
//! Kitsune2 itself.
//!
//! That being said, the bootstrap server and client are designed to transfer
//! the `AgentInfoSigned` data type defined in the `kitsune2_api` crate. Since
//! the canonical encoding of that type is JSON, we just redefine a subset
//! of the schema here, and only validate the parts required for bootstrapping.
//!
//! For additional details, please see the [spec].
/// This is a documentation module containing the kitsune2_bootstrap spec.
///
/// #### 1. Types
///
/// All base64 in this spec uses: <https://datatracker.ietf.org/doc/html/rfc4648#section-5>
///
/// The url safe alphabet (with `-` and `_`) and no padding characters (`=`)
/// added when encoding or required when decoding.
///
/// - `Base64Agent` - base64UrlSafeNoPad encoded string agent id.
/// - `Base64Space` - base64UrlSafeNoPad encoded string space id.
/// - `Base64Sig` - base64UrlSafeNoPad encoded string crypto signature.
/// - `Json` - string containing json that can be decoded.
/// - `I64` - string containing an i64 number indicating the number of
/// microseconds since the unix epoch. Since the unix epoch
/// is canonically defined in UTC, the timestamp is also in UTC.
///
/// ```text
/// AgentInfoSigned = { "agentInfo": Json, "signature": Base64Sig }
/// AgentInfo = {
/// "agent": Base64Agent,
/// "space": Base64Space,
/// "createdAt": I64,
/// "expiresAt": I64,
/// "isTombstone": boolean
/// }
/// ```
///
/// Any other properties on these objects will be ignored and pass through.
///
/// A note on the "isTombstone" field: This is named "tombstone" because the
/// end-user nodes using this bootstrap service will be exchanging infos
/// directly and must keep these tombstone records around to prevent older
/// but not-yet-expired entries from re-appearing. In the nominal case for
/// bootstrapping, only the author is publishing these entries, so we don't
/// need to worry about any re-appearance. We believe the 3 minute acceptance
/// window mitigates any griefing attempt sufficiently, but a server
/// implementation MAY keep a short list of tombstones around if it notices
/// the entry it is deleting is still valid.
///
/// #### 2. REST API
///
/// ##### 2.1. In Brief
///
/// ```text
/// ErrResponse = { "error": string }
/// OkResponse = {}
/// ListResponse = [ AgentInfoSigned, .. ]
/// ```
///
/// - `PUT /bootstrap/<Base64Space>/<Base64Agent>`
/// - Request Body: `AgentInfoSigned`
/// - Response Body: `OkResponse | ErrResponse`
/// - `GET /bootstrap/<Base64Space>`
/// - Response Body: `ListResponse | ErrResponse`
/// - `GET /health`
/// - Response Body: `OkResponse | ErrResponse`
///
/// ##### 2.2. Publishing info to the bootstrap server.
///
/// A `PUT` on `/bootstrap/<Base64Space>/<Base64Agent>` with an
/// `AgentInfoSigned` json object as the request body.
///
/// - The server MUST reject the request if the body is > 1024 bytes.
/// - The server MUST reject the request if `createdAt` is not within
/// 3 minutes in either direction of the server time.
/// - The server MUST reject the request if `expiresAt` is in the past.
/// - The server MUST reject the request if `expiresAt <= createdAt`.
/// - The server MUST reject the request if `expiresAt - createdAt` is
/// more than 30 minutes.
/// - The server MUST reject the request if `signature` is invalid vs
/// the `agentInfo`.
/// - If `isTombstone` is `true`, the server MUST delete any existing
/// info being held matching the `space` and `agent` and with a `createdAt`
/// less than the `createdAt` of the tombstone record.
/// - If `isTombstone` is `false`, the server MAY begin storing the info.
/// See section 3. on storage strategies below.
///
/// ##### 2.3. Listing data stored on the bootstrap server.
///
/// A `GET` on `/bootstrap/<Base64Space>`.
///
/// - The server MUST respond with a complete list of stored infos.
/// - If there are no infos stored at this space, the server MUST return
/// an empty list (`[]`).
/// - The only reason a server MAY return an `ErrResponse` for this request
/// is in the case of internal server error.
///
/// ##### 2.4. Health check.
///
/// A `GET` on `/health`.
///
/// - The server, in general, SHOULD return `OkResponse` to this request.
/// - The server MAY return `ErrResponse` for internal errors or some other
/// inability to continue serving correctly.
///
/// #### 3. Storage Strategies
///
/// ##### 3.1. The Future
///
/// It is the intention someday in the future to add a "trusted" strategy,
/// that will be triggerd via a new api, perhaps `/registerTrust/Base64Space`.
///
/// Even when that API is implemented, however, the default strategy defined
/// next will be used on all spaces that have not been registered with a
/// different strategy. Therefore, we can proceed for now without consideration
/// for any other future strategies.
///
/// ##### 3.2. The "Default" Storage Strategy
///
/// Assumptions:
///
/// - We don't want to store unbounded count infos in each space.
/// - We like storing long-running reliable nodes.
/// - We don't want to ONLY store long-running nodes to avoid eclipse scenarios.
///
/// The solution put forth here is to allocate half our storage space to
/// long-running nodes, and the other half to whoever has most recently
/// published an info. The strategy for accomplishing this is defined here:
///
/// Consider "the store" acting as a stack. It can be implemented in any manner,
/// but the following strategy assumes new entries are added to the end,
/// and when any entries are removed, the indexes of any items after them
/// will be decrimented.
///
/// - The server SHOULD allow setting the maximum number of infos that will be
/// allowed in a single space.
/// For the duration of this document, that value will be called MAX_INFOS.
/// It is recommended to default that value to `32` to keep the maximum
/// response size from a request to around 32KiB.
/// - The server SHOULD delete expired infos periodically.
/// - If the server is already storing an agent, and a `PUT` with a newer
/// `createdAt` arrives, the existing entry MUST be replaced.
/// - If the store count is < MAX_INFOS, the server MUST push the info
/// onto the stack.
/// - If the store count is >= MAX_INFOS, the server MUST delete the entry
/// at MAX_INFOS / 2 and then push the info onto the stack.
///
/// #### 4. Rate Limiting
///
/// Info count within a space is limited by the storage strategy above. But
/// space count within a server is unbounded. While servers will likely want
/// to limit request counts in general, we are also going to have to take some
/// special care with PUT requests that result in creation of a new space that
/// was not previously tracked in order to mitigate attacks.
///
/// - A server SHOULD delete any spaces that no longer contain agents.
/// - A server SHOULD decide a max count of spaces it intends to support
/// (based on available memory or disk space for storing agents within those
/// spaces), and then return error 429 beyond that.
/// - A server MAY make the above max space count configurable.
/// - A server SHOULD track the IP addresses of clients that make PUT requests
/// which result in space creation, and error with 429 if that frequency
/// is beyond a limit.
/// - A server MAY make this limit configurable.
///
/// #### 5. Client Recommendations
///
/// - In the case of server unreachable or 500 error, a client should use
/// an exponential backoff strategy to avoid stressing the server when
/// it does come back online.
/// - A client should poll the bootstrap server on an interval >= 5 minutes
/// to balance getting new infos to avoid partitioning without worry
/// of hitting the rate limit.
/// - A client, in general, should set expiration to 20 minutes beyond
/// the info creation time.
/// - A client, in general, should re-publish a new info on a 15 minute
/// interval.
pub use *;
use *;
use *;
use *;
use *;
pub use *;
pub use *;