Repository navigation
Expand file tree
/
Copy pathConfigSchema.ts
More file actions
343 lines (310 loc) · 8.92 KB
/
Copy pathConfigSchema.ts
File metadata and controls
343 lines (310 loc) · 8.92 KB
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
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
import z from "zod";
import { InstanceBehaviour } from "./activitypub/instanceBehaviour.js";
import { LogLevel } from "./log.js";
const RateLimitSchema = z.object({
/**
* Milliseconds
* @default 15 minutes
*/
window: z.number().default(15 * 60 * 1000),
/**
* Number of requests per window.
* @default Default: 100
*/
limit: z.number().default(100),
});
export const ConfigSchema = z.object({
general: z
.object({
/**
* The name of this server
*/
name: z.string(),
/**
* URL for this servers terms
*/
terms: z.url(),
})
.partial()
.prefault({}),
/**
* Shoots own logging configuration
*/
log: z
.object({
/**
* Maximum level of logs to print
* see LogLevel enum for values
* @default "verbose"
*/
level: z.enum(LogLevel).default(LogLevel.verbose),
/**
* Whether or not to include dates in log messages
*/
include_date: z.boolean().default(true),
})
.prefault({}),
http: z
.object({
/**
* Whether to enable morgan logging of http requests.
* @example `200, 404` will log requests with status codes 200, 400
* @example `-200` will log all non-200 responses
* @example `-` will log all responses
* @example `-200, 404` will log all codes except 200, 404
*/
log: z.string().optional(),
/**
* Log format to use when morgan logging is enabled
* @see https://www.npmjs.com/package/morgan#predefined-formats
* @default "combined"
*/
log_format: z.string().default("combined"),
/**
* Rate limiter for incoming HTTP API requests.
*/
rate: z
.object({
s2s: RateLimitSchema.prefault({}),
auth: RateLimitSchema.prefault({}),
nodeinfo: RateLimitSchema.prefault({}),
wellknown: RateLimitSchema.prefault({}),
global: RateLimitSchema.prefault({}),
})
.prefault({}),
})
.prefault({}),
security: z.object({
/**
* The Jsonwebtoken secret used to generate authentication tokens.
* Generate with `crypto.randomBytes(256).toString("base64")` or `npm run cli -- generate-keys`
*/
jwt_secret: z.string(),
/**
* How to determine the client IP when behind a proxy
* https://expressjs.com/en/guide/behind-proxies.html
*
* @default "loopback,uniquelocal"
*/
trust_proxy: z.string().default("loopback,uniquelocal"),
/**
* Whether to enable dynamic OAuth client registration.
* If disabled, client applications cannot dynamically register on this instance
* Turn this off if you want to only allow a specific client application to connect.
* You'll need to give the client the client_id and client_secret.
*/
dynamic_client_registration: z.boolean().default(true),
}),
database: z.object({
/**
* A URL style database connection string
* @example `mysql://username:password@address:port/database_name`
*/
url: z.url(),
/**
* Whether to log database operations.
*/
log: z.boolean().default(false),
}),
federation: z
.object({
/**
* Is federation enabled?
* If disabled, foreign network features will be unavailable.
*/
enabled: z.boolean().default(false),
/**
* The URL of the webapp for this instance.
* If not hosting a webapp, set this to the same value as instance_url
*/
webapp_url: z.url().transform((x) => new URL(x)),
/**
* The URL of this instance. Required.
*/
instance_url: z.url().transform((x) => new URL(x)),
/**
* Aka, authorised fetch. Require HTTP signatures from remote servers for all requests
* If disabled, only require when absolutely necessary such as when receiving a new chat message,
* while reading public data will still be allowed.
*/
require_http_signatures: z.boolean().default(false),
/**
* Various settings related to the federation queues implemented using Redis.
* You can configure Redis via the `redis.*` config options.
*/
queue: z
.object({
/**
* Whether or not to use the inbound queue. Requires redis.
* @default false
*/
use_inbound: z.boolean().default(false),
})
.prefault({}),
/**
* The public and private keys of the instance actor (/actor)
* used for verifying and signing HTTP signatures
*/
public_key: z.string(),
private_key: z.string(),
/**
* Control whether the `federation.instances` property is a denylist or allowlist
*
* @default false
*/
allowlist: z.boolean().default(false),
/**
* Control how Shoot handles requests from different instances
* This is a key-value store where the key is `new URL(instance).origin`
* and the value is the desired behaviour defined by InstanceBehaviour
*
* TODO it would be good if we could provide reasons for these
*/
instances: z.record(z.string(), z.enum(InstanceBehaviour)).default({}),
})
.prefault({
webapp_url: "http://localhost",
instance_url: "http://localhost",
public_key: "",
private_key: "",
enabled: false,
}),
webrtc: z
.object({
/**
* If Webrtc is disabled, voice calls will not be allowed and the signalling server
* will not be started.
*/
enabled: z.boolean().default(false),
/** Janus gateway api secret */
janus_secret: z.string().optional(),
/**
* Janus gateway url. Websocket, http, or unix socket
* @default "ws://localhost:8188"
*/
janus_url: z.url().default("ws://localhost:8188"),
signal_address: z.url().optional(),
})
.prefault({}),
registration: z
.object({
/**
* If registration is disabled, new users cannot be created from the API.
* The CLI can still be used to create users using admin operations.
*/
enabled: z.boolean().default(false),
/**
* List of guilds to automatically join when a user registers
* Even if registration is disabled, this setting is still followed when registration tokens are used.
* The guilds listed here must be local guilds.
* @default []
*/
auto_join: z.uuid().array().default([]),
})
.prefault({}),
storage: z
.object({
/**
* The local directory to upload user content.
* If storage.s3.* is defined and valid, s3 will be used instead
* @default "./storage"
*/
directory: z.string().default("./storage"),
/**
* Maximum size of user uploaded content in bytes
* @default 10mb
*/
max_file_size: z.number().default(1024 * 1024 * 10),
s3: z
.object({
enabled: z.boolean().default(false),
/**
* The endpoint to use for s3 storage.
* If not provided, uses aws s3
*/
endpoint: z.string().optional(),
region: z.string(),
bucket: z.string(),
accessKey: z.string(),
secret: z.string(),
/**
* Whether to force path style URLs for S3 objects
* (e.g., https://s3.amazonaws.com/{bucket}/ instead of https://{bucket}.s3.amazonaws.com/
*/
forcePathStyle: z.boolean().default(false),
})
.prefault({
region: "",
bucket: "",
accessKey: "",
secret: "",
}),
})
.prefault({}),
/**
* Redis is optional. It is currently only used for the inbound federation queue
*/
redis: z
.object({
/**
* The IP/hostname of the Redis instance to connect to
*
* @default "localhost"
*/
host: z.string().default("localhost"),
/**
* The port to use when connecting to a Redis host
*
* @default 6379
*/
port: z.number().default(6379),
})
.prefault({}),
/**
* RabbitMQ is optional.
* It is required if you:
* - use the inbound federation queue
* - run api, gateway individually instead of as one process (i.e. via `npm run start:http` etc)
*
* It is used for sending events from the API, among other components, to the gateway
* when they do not share memory
*/
rabbitmq: z
.object({
enabled: z.boolean().default(false),
url: z.url().transform((x) => new URL(x)),
})
.prefault({ url: "http://localhost" }),
/**
* Media proxy settings. Shoot supports Imagor/Thumbor for proxying remote images.
* While optional, it's recommended this is enabled as it:
* - prevents simple IP grabbing of clients, or whatever other data is leaked when fetching media
* - prevents effectively DDOSing remote servers hosting media when being requests by many clients
*/
media_proxy: z
.object({
enabled: z.boolean().default(false),
/**
* The public endpoint of the imagor proxy server. Clients will connect to this address.
*/
url: z.url(),
/**
* Optional signing key to prevent users from generating their own media proxy requests
*/
secret: z.string().optional(),
})
.prefault({ url: "http://localhost" }),
/**
* Push notifications via the Web Push API.
* Generate a public and private key via https://github.com/web-push-libs/web-push
* `npx web-push generate-vapid-keys`
*/
notifications: z
.object({
enabled: z.boolean().default(false),
privateKey: z.string(),
publicKey: z.string(),
})
.prefault({ publicKey: "", privateKey: "" }),
});
export type ConfigSchema = z.infer<typeof ConfigSchema>;