diff options
Diffstat (limited to 'man')
| -rw-r--r-- | man/witryna.toml.5 | 490 |
1 files changed, 490 insertions, 0 deletions
diff --git a/man/witryna.toml.5 b/man/witryna.toml.5 new file mode 100644 index 0000000..29c0331 --- /dev/null +++ b/man/witryna.toml.5 @@ -0,0 +1,490 @@ +.TH WITRYNA.TOML 5 "2026-02-10" "witryna 0.1.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, rate limiting, 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) +Tracing verbosity. +Valid values: "trace", "debug", "info", "warn", "error" (case\-insensitive). +Can be overridden at runtime with the \fBRUST_LOG\fR environment variable. +.TP +\fBrate_limit_per_minute\fR = \fIn\fR (optional, default: 10) +Maximum webhook requests per token per minute. +Exceeding this limit returns HTTP 429. +.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). +.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 a successful symlink switch. +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). +.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: the deployment is already live, +and a warning is logged. +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" + +# Webhook rate limit (per token, per minute) +rate_limit_per_minute = 10 + +# 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) |
