summaryrefslogtreecommitdiff
path: root/man/witryna.toml.5
blob: 78a6ece2014f2538a9fcdf1e5e3a26195c4f2f2e (plain)
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
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
.TH WITRYNA.TOML 5 "2026-02-15" "witryna 0.2.0" "Witryna Configuration"
.SH NAME
witryna.toml \- configuration file for \fBwitryna\fR(1)
.SH DESCRIPTION
\fBwitryna.toml\fR is a TOML file that configures the \fBwitryna\fR static site
deployment orchestrator.
It defines the HTTP listen address, container runtime, directory layout,
logging, and zero or more site definitions with optional build
overrides, polling intervals, cache volumes, and post\-deploy hooks.
.PP
The file is read at startup and can be reloaded at runtime by sending
\fBSIGHUP\fR to the process (see \fBHOT RELOAD\fR below).
.SH GLOBAL OPTIONS
.TP
\fBlisten_address\fR = "\fIhost:port\fR" (required)
Socket address the HTTP server binds to.
Must be a valid \fIip:port\fR pair (e.g., "127.0.0.1:8080").
.TP
\fBcontainer_runtime\fR = "\fIname\fR" (required)
Container runtime executable used to run build commands.
Typically "podman" or "docker".
Must not be empty or whitespace\-only.
.TP
\fBbase_dir\fR = "\fI/path\fR" (required)
Root directory for clones, builds, and cache.
Default layout:
.RS
.nf
<base_dir>/clones/<site>/
<base_dir>/builds/<site>/<timestamp>/
<base_dir>/builds/<site>/current -> <timestamp>
<base_dir>/cache/<site>/
.fi
.RE
.TP
\fBlog_dir\fR = "\fI/path\fR" (optional, default: "/var/log/witryna")
Directory for per\-build log files.
Layout: \fI<log_dir>/<site>/<timestamp>.log\fR
.TP
\fBlog_level\fR = "\fIlevel\fR" (required)
Log verbosity.
Valid values: "trace", "debug", "info", "warn", "error" (case\-insensitive).
Can be overridden at runtime with the \fBRUST_LOG\fR environment variable.
.PP
\fBNote:\fR The server enforces a hard 1\ MB request body size limit.
This is not configurable and applies to all endpoints.
.TP
\fBmax_builds_to_keep\fR = \fIn\fR (optional, default: 5)
Number of timestamped build directories to retain per site.
Older builds and their corresponding log files are removed after each
successful publish.
Set to 0 to disable cleanup (keep all builds).
See also: \fBwitryna cleanup\fR for manual pruning.
.TP
\fBgit_timeout\fR = "\fIduration\fR" (optional, default: "1m")
Maximum time allowed for each git operation (clone, fetch, reset, submodule update).
Accepts a \fBhumantime\fR duration string (e.g., "30s", "2m", "5m").
.RS
Minimum is 5 seconds, maximum is 1 hour.
Applies globally to all sites.
Repositories with many or large submodules may need a longer timeout (e.g., "5m").
.RE
.SH SITE DEFINITIONS
Sites are defined as TOML array\-of\-tables entries under \fB[[sites]]\fR.
Each site represents a Git repository that witryna can build and publish.
Site names must be unique.
The list may be empty (\fBsites = []\fR); witryna will start but serve
only the health\-check endpoint.
.TP
\fBname\fR = "\fIsite\-name\fR" (required)
Unique identifier for the site.
Used in the webhook URL path (\fIPOST /<name>\fR) and directory names.
.RS
Validation rules:
.IP \(bu 2
Alphanumeric characters, hyphens, and underscores only.
.IP \(bu 2
Cannot start or end with a hyphen or underscore.
.IP \(bu 2
Cannot contain consecutive hyphens or consecutive underscores.
.IP \(bu 2
No path traversal characters (\fI..\fR, \fI/\fR, \fI\\\fR).
.RE
.TP
\fBrepo_url\fR = "\fIurl\fR" (required)
Git repository URL to clone.
Any URL that \fBgit clone\fR accepts (HTTPS, SSH, local path).
.TP
\fBbranch\fR = "\fIref\fR" (required)
Git branch to track.
This branch is checked out after clone and fetched on each build trigger.
.TP
\fBwebhook_token\fR = "\fItoken\fR" (optional)
Bearer token for webhook endpoint authentication.
If omitted or set to an empty string, webhook authentication is disabled for
this site \(em all POST requests to the site endpoint will be accepted without
token validation.
Use this when the endpoint is protected by other means (reverse proxy, VPN,
firewall).
A warning is logged at startup for sites without authentication.
.PP
When set, the token is validated using constant\-time comparison to prevent
timing attacks.
Sent as: \fIAuthorization: Bearer <token>\fR.
.RS
The token can be provided in three ways:
.IP \(bu 2
\fBLiteral value:\fR \fBwebhook_token = "my\-secret"\fR
.IP \(bu 2
\fBEnvironment variable:\fR \fBwebhook_token = "${VAR_NAME}"\fR \- resolved from
the process environment at config load time.
The variable name must consist of ASCII uppercase letters, digits, and underscores,
and must start with a letter or underscore.
Only full\-value substitution is supported; partial interpolation
(e.g., "prefix\-${VAR}") is treated as a literal token.
.IP \(bu 2
\fBFile:\fR Use \fBwebhook_token_file\fR (see below).
.PP
The \fB${VAR}\fR syntax and \fBwebhook_token_file\fR are mutually exclusive.
If the referenced environment variable is not set or the file cannot be read,
config loading fails with an error.
.RE
.TP
\fBwebhook_token_file\fR = "\fI/path/to/file\fR" (optional)
Path to a file containing the webhook token.
The file contents are read and trimmed of leading/trailing whitespace.
Compatible with Docker secrets (\fI/run/secrets/\fR) and Kubernetes secret volumes.
.RS
When set, \fBwebhook_token\fR should be omitted (it defaults to empty).
Cannot be combined with the \fB${VAR}\fR substitution syntax.
.PP
\fBSecurity note:\fR Ensure the token file has restrictive permissions
(e.g., 0400 or 0600) and is readable only by the witryna user.
.RE
.SH REPOSITORY CONFIG FILE
.TP
\fBconfig_file\fR = "\fIpath\fR" (optional)
Path to a custom build config file in the repository, relative to the repo root
(e.g., ".witryna.yaml", "build/config.yml").
Must be a relative path with no path traversal (\fI..\fR).
.PP
If not set, witryna searches the repository root in order:
\fI.witryna.yaml\fR, \fI.witryna.yml\fR, \fIwitryna.yaml\fR, \fIwitryna.yml\fR.
The first file found is used.
.SH BUILD OVERRIDES
Build parameters can optionally be specified directly in the site definition,
overriding values from the repository's build config file
(\fI.witryna.yaml\fR / \fIwitryna.yaml\fR).
When all three fields (\fBimage\fR, \fBcommand\fR, \fBpublic\fR) are set,
the repository config file becomes optional.
.TP
\fBimage\fR = "\fIcontainer:tag\fR" (optional)
Container image to use for the build (e.g., "node:20\-alpine").
Required unless provided in \fIwitryna.yaml\fR.
Must not be blank.
.TP
\fBcommand\fR = "\fIshell command\fR" (optional)
Build command executed via \fBsh \-c\fR inside the container.
Must not be blank.
.TP
\fBpublic\fR = "\fIrelative/path\fR" (optional)
Directory containing built static assets, relative to the repository root.
Must be a relative path with no path traversal (\fI..\fR).
.SH RESOURCE LIMITS
Optional resource limits for container builds.
These flags are passed directly to the container runtime.
.TP
\fBcontainer_memory\fR = "\fIsize\fR" (optional)
Memory limit for the build container (e.g., "512m", "2g", "1024k").
Must be a number followed by a unit suffix: \fBk\fR, \fBm\fR, or \fBg\fR
(case\-insensitive).
Passed as \fB\-\-memory\fR to the container runtime.
.TP
\fBcontainer_cpus\fR = \fIn\fR (optional)
CPU limit for the build container (e.g., 0.5, 2.0).
Must be greater than 0.
Passed as \fB\-\-cpus\fR to the container runtime.
.TP
\fBcontainer_pids_limit\fR = \fIn\fR (optional)
Maximum number of PIDs inside the build container (e.g., 100).
Must be greater than 0.
Passed as \fB\-\-pids\-limit\fR to the container runtime.
Helps prevent fork bombs during builds.
.SH NETWORK ISOLATION
.TP
\fBcontainer_network\fR = "\fImode\fR" (optional, default: "bridge")
Network mode for the build container.
Passed as \fB\-\-network=\fImode\fR to the container runtime.
.RS
Allowed values:
.IP \(bu 2
\fB"bridge"\fR (default) \- Standard container networking (NAT).
Works out of the box for builds that download dependencies (e.g., \fBnpm install\fR).
.IP \(bu 2
\fB"none"\fR \- No network access.
Most secure option; use for builds that don't need to download anything.
.IP \(bu 2
\fB"host"\fR \- Use the host network namespace directly.
.IP \(bu 2
\fB"slirp4netns"\fR \- User\-mode networking (Podman rootless).
.PP
Set \fBcontainer_network = "none"\fR for maximum isolation when your build
does not require network access.
.RE
.SH CONTAINER WORKING DIRECTORY
.TP
\fBcontainer_workdir\fR = "\fIpath\fR" (optional, default: repo root)
Working directory inside the build container, relative to the repository root.
Useful for monorepo projects where the build runs from a subdirectory.
.PP
The value is a relative path (e.g., "packages/frontend") appended to the
default \fB/workspace\fR mount point, resulting in
\fB\-\-workdir /workspace/packages/frontend\fR.
.PP
Must be a relative path with no path traversal (\fI..\fR) and no leading slash.
.SH POLLING
.TP
\fBpoll_interval\fR = "\fIduration\fR" (optional, default: disabled)
If set, witryna periodically fetches the remote branch and triggers a build
when new commits are detected.
Accepts a \fBhumantime\fR duration string (e.g., "30m", "1h", "2h30m").
.RS
Minimum interval is 1 minute.
Polling respects the concurrent build lock; if a build is already in progress,
the poll cycle is skipped.
Initial poll delays are staggered across sites to avoid a thundering herd.
.RE
.SH GIT CLONE DEPTH
.TP
\fBgit_depth\fR = \fIn\fR (optional, default: 1)
Git clone depth for the repository.
.RS
.IP \(bu 2
\fB1\fR (default) \- Shallow clone with only the latest commit.
Fast and minimal disk usage.
.IP \(bu 2
\fB0\fR \- Full clone with complete history.
Required for \fBgit describe\fR or monorepo change detection.
.IP \(bu 2
\fBn > 1\fR \- Shallow clone with \fIn\fR commits of history.
.RE
.PP
Submodules are detected and initialized automatically regardless of clone depth.
.SH BUILD TIMEOUT
.TP
\fBbuild_timeout\fR = "\fIduration\fR" (optional, default: "10m")
Maximum time a build command is allowed to run before being killed.
Accepts a \fBhumantime\fR duration string (e.g., "5m", "30m", "1h").
.RS
Minimum is 10 seconds, maximum is 24 hours.
.RE
.SH CACHE VOLUMES
.TP
\fBcache_dirs\fR = ["\fI/container/path\fR", ...] (optional)
List of absolute container paths to persist as cache volumes across builds.
Each path gets a dedicated host directory under \fI<base_dir>/cache/<site>/\fR.
.RS
Validation rules:
.IP \(bu 2
Each entry must be an absolute path.
.IP \(bu 2
No path traversal (\fI..\fR) allowed.
.IP \(bu 2
No duplicates after normalization.
.IP \(bu 2
Maximum 20 entries per site.
.PP
Common cache paths:
.TS
l l.
Node.js	/root/.npm
Python pip	/root/.cache/pip
Go modules	/root/.cache/go
Rust cargo	/usr/local/cargo/registry
Maven	/root/.m2/repository
.TE
.PP
Cache directories are \fBnever cleaned automatically\fR by witryna.
Administrators should monitor disk usage under
\fI<base_dir>/cache/\fR and prune manually when needed.
.RE
.SH ENVIRONMENT VARIABLES
.TP
\fB[sites.env]\fR (optional)
TOML table of environment variables passed to builds and post\-deploy hooks.
Each key\-value pair becomes a \fB\-\-env KEY=VALUE\fR flag for the container
runtime.
Variables are also passed to post\-deploy hooks.
.RS
Validation rules:
.IP \(bu 2
Keys must not be empty.
.IP \(bu 2
Keys must not contain \fB=\fR.
.IP \(bu 2
Neither keys nor values may contain null bytes.
.IP \(bu 2
Keys starting with \fBWITRYNA_\fR (case\-insensitive) are reserved and rejected.
.IP \(bu 2
Maximum 64 entries per site.
.PP
In post\-deploy hooks, user\-defined variables are set \fBbefore\fR the reserved
variables (PATH, HOME, LANG, WITRYNA_*), so they cannot override system or
Witryna\-internal values.
.RE
.SH POST-DEPLOY HOOKS
.TP
\fBpost_deploy\fR = ["\fIcmd\fR", "\fIarg\fR", ...] (optional)
Command to execute after every build completion (success or failure).
Uses array form (no shell interpolation) for safety.
.RS
The hook receives context exclusively via environment variables:
.TP
\fBWITRYNA_SITE\fR
The site name.
.TP
\fBWITRYNA_BUILD_DIR\fR
Absolute path to the new build directory (also the working directory).
.TP
\fBWITRYNA_PUBLIC_DIR\fR
Absolute path to the stable current symlink
(e.g. /var/lib/witryna/builds/my\-site/current).
Use this as the web server document root.
.TP
\fBWITRYNA_BUILD_TIMESTAMP\fR
Build timestamp (YYYYmmdd-HHMMSS-ffffff).
.TP
\fBWITRYNA_BUILD_STATUS\fR
Build outcome: "success" or "failed".
.PP
The hook runs with a minimal environment: any user\-defined variables
from \fB[sites.env]\fR, followed by PATH, HOME, LANG,
and the WITRYNA_* variables above (which take precedence).
Its working directory is the build output directory.
It is subject to a 30\-second timeout and killed if exceeded.
Output is streamed to disk and logged to
\fI<log_dir>/<site>/<timestamp>\-hook.log\fR.
.PP
Hook failure is \fBnon\-fatal\fR and a warning is logged.
On successful builds the deployment is already live; on failed builds
the hook still runs but no assets were published.
The exit code is recorded in the hook log.
A log file is written for every hook invocation (success, failure,
timeout, or spawn error).
.PP
Validation rules:
.IP \(bu 2
The array must not be empty.
.IP \(bu 2
The first element (executable) must not be empty or whitespace\-only.
.IP \(bu 2
No element may contain null bytes.
.IP \(bu 2
Maximum 64 elements.
.RE
.SH HOT RELOAD
Sending \fBSIGHUP\fR to the witryna process causes it to re\-read
\fIwitryna.toml\fR.
Sites can be added, removed, or reconfigured without downtime.
Polling tasks are stopped and restarted to reflect the new configuration.
.PP
The following fields are \fBnot reloadable\fR and require a full restart:
.IP \(bu 2
\fBlisten_address\fR
.IP \(bu 2
\fBbase_dir\fR
.IP \(bu 2
\fBlog_dir\fR
.IP \(bu 2
\fBlog_level\fR
.PP
If any of these fields differ after reload, a warning is logged but the new
values are ignored until the process is restarted.
.PP
If the reloaded configuration is invalid, the existing configuration remains
active and an error is logged.
.SH EXAMPLES
A complete annotated configuration:
.PP
.nf
.RS 4
# Network
listen_address = "127.0.0.1:8080"

# Container runtime ("podman" or "docker")
container_runtime = "podman"

# Data directory (clones, builds, cache)
base_dir = "/var/lib/witryna"

# Log directory (per\-build logs)
log_dir = "/var/log/witryna"

# Tracing verbosity
log_level = "info"

# Keep the 5 most recent builds per site
max_builds_to_keep = 5

# Git operation timeout (clone, fetch, reset)
# git_timeout = "2m"

# A site that relies on witryna.yaml in the repo
[[sites]]
name = "my\-blog"
repo_url = "https://github.com/user/my\-blog.git"
branch = "main"
webhook_token = "s3cret\-tok3n"
# Or from environment: webhook_token = "${MY_BLOG_TOKEN}"
# Or from file: webhook_token_file = "/run/secrets/blog\-token"
poll_interval = "1h"

# A site with full build overrides (no witryna.yaml needed)
# Custom build timeout (default: 10 minutes)
[[sites]]
name = "docs\-site"
repo_url = "https://github.com/user/docs.git"
branch = "main"
webhook_token = "an0ther\-t0ken"
image = "node:20\-alpine"
command = "npm ci && npm run build"
public = "dist"
build_timeout = "30m"
cache_dirs = ["/root/.npm"]
post_deploy = ["curl", "\-sf", "https://status.example.com/deployed"]

# Environment variables for builds and hooks
[sites.env]
DEPLOY_TOKEN = "abc123"
NODE_ENV = "production"
.fi
.RE
.SH SECURITY CONSIDERATIONS
.TP
\fBContainer isolation\fR
Build commands run inside ephemeral containers with \fB\-\-cap\-drop=ALL\fR.
When the runtime is Podman, \fB\-\-userns=keep\-id\fR is added so the
container user maps to the host UID, avoiding permission issues without
any extra capabilities.
When the runtime is Docker, \fBDAC_OVERRIDE\fR is re\-added because
Docker runs as root (UID\ 0) inside the container while the workspace is
owned by the host UID; without this capability, the build cannot read or
write files.
The container filesystem is isolated; the build has no direct access to
the host beyond the mounted workspace directory.
.SH SYSTEMD OVERRIDES
The deb and rpm packages install example systemd override templates to
\fI/usr/share/doc/witryna/examples/systemd/\fR.
The post\-install script copies the appropriate template to
\fI/etc/systemd/system/witryna.service.d/10\-runtime.conf\fR based on the
detected container runtime.
.TP
\fBdocker.conf\fR
.nf
.RS 4
[Service]
SupplementaryGroups=docker
ReadWritePaths=/var/run/docker.sock
.fi
.RE
.TP
\fBpodman.conf\fR
.nf
.RS 4
[Service]
RestrictNamespaces=no
Environment="XDG_RUNTIME_DIR=/run/user/%U"
.fi
.RE
.PP
\fB%U\fR resolves to the numeric UID of the service user at runtime.
To add custom overrides, create a separate file (e.g.,
\fI20\-custom.conf\fR) in the same directory; do not edit
\fI10\-runtime.conf\fR as it will be overwritten on package reinstall.
.SH FILES
When no \fB\-\-config\fR flag is given, \fBwitryna\fR searches for the
configuration file in the following order:
.IP 1. 4
\fI./witryna.toml\fR (current working directory)
.IP 2. 4
\fI$XDG_CONFIG_HOME/witryna/witryna.toml\fR (default: \fI~/.config/witryna/witryna.toml\fR)
.IP 3. 4
\fI/etc/witryna/witryna.toml\fR
.PP
The first file found is used.
If none exists, an error is printed listing all searched paths.
.SH SEE ALSO
\fBwitryna\fR(1)