Mercurial Hosting > d2o
view man/d2obase.5.md @ 8:2ffb8028ccbb
add loggingmodes and fix path matching
| author | Atarwn Gard <a@qwa.su> |
|---|---|
| date | Tue, 17 Mar 2026 19:55:07 +0500 |
| parents | 54ab94198677 |
| children |
line wrap: on
line source
% D2OBASE(5) % March 2026 # NAME d2obase - syntax and configuration guide for the d2o web server # DESCRIPTION The **ICF** (Inheritance Config Format) uses a stream-oriented approach to web serving. It focuses on reducing "boilerplate" by using variables, reusable mixins, and execution pipelines. # SYNTAX A configuration file consists of variable assignments, mixin definitions, and site blocks containing pipelines. ## Variables Variables are defined as `KEY=VALUE`. They are global and can be referenced later using the `$` prefix (e.g., `$DOM`). Variable names may only contain characters `[A-Za-z0-9_]`. Values are substituted at the point of declaration, so a variable can reference only those variables defined above it in the file. ## Mixins (Templates) A mixin is defined with the `@` prefix. It stores a set of pipeline directives that can be "splatted" into a site block to avoid repetition. @name |> directive argument Mixins can inherit from other mixins: @child @parent |> directive argument The parent's directives are inserted before the child's, so the child can override them. Circular references are silently ignored. ## Pipelines and Directives The core of the configuration is the pipeline. Each line starting with `|>` is a directive executed in top-down order. Directives follow the format: `|> directive [arguments...]`. ### Argument Expansion Directives support curly brace expansion `{}`. For example: `file.{crt,key}` expands to two separate arguments: `file.crt` and `file.key`. Only one pair of braces per argument is expanded. Nested braces are not supported. ## Site Blocks A site block starts with a domain name (or a variable containing one), followed by optional mixins, and a pipeline. example.org @setup |> directive argument ### Pattern Matching The domain part of a site block is matched exactly. The path part (if present, separated by `/`) is matched by prefix. Named capture groups can be used in patterns with `<name>` syntax. The captured value is available as `$name` in the block's directives. <sub>.example.org |> root /srv/www/$sub #### Block Specificity When multiple blocks match a request, the most specific one wins. Specificity is the number of literal characters matched in the pattern - capture groups `<n>` do not contribute to the score. Host and path are scored separately; host score is weighted heavier (`score = host_score × 1000 + path_score`). For example, given these two blocks: app.example.org |> rprx http://127.0.0.1:8080 <sub>.example.org |> root /srv/www/$sub A request for `app.example.org` matches both, but `app.example.org` wins - it matched 15 literal characters against 11 for `<sub>.example.org` (`.example.org` only). Order of declaration in the file does not matter. ## Comments Lines beginning with `;` are ignored. Inline comments start with ` ;` (space before semicolon). ; full line comment KEY=value ; inline comment # DIRECTIVES ## Listening **port** *number* : Listen for plain HTTP on the given port. **port+tls** *number* [*cert* *key*] : Listen for HTTPS on the given port. Certificate and key paths are optional if a **tls** directive is present in the same block. **tls** *cert* *key* : Set the default certificate and key for **port+tls** directives in this block that do not specify their own paths. ## Serving **root** *path* [**show**] : Serve static files from *path*. Without **show**, directory listing is forbidden (403). With **show**, an HTML directory listing is returned. **ndex** *file...* : List of index filenames to try when a directory is requested, checked left to right. If a matching file passes the **fcgi** pattern, it is handled by FastCGI instead of served directly. **fcgi** *address* [*pattern*] : Forward matching requests to a FastCGI server. *address* is either `unix:///path/to/socket` or `host:port`. *pattern* is a glob matched against the request path (default `*`). When used together with **root**, only requests matching the pattern are forwarded; everything else is served as static. **rprx** *address* : Reverse-proxy all requests to *address*. The `http://` scheme is assumed if not specified. **rdir** *code* *url* : Redirect to *url* with the given HTTP status code. If *code* is omitted or zero, 302 is used. ## Global (in `@d2o` block only) **threads** *n* : Set `GOMAXPROCS` to *n*. # EXAMPLES Below is a complex setup using global variables, a shared mixin for PHP sites, and subdomain handling: DOM=qwaderton.org ACME=/etc/acme WWW=/srv/www/$DOM @ports |> port 80 |> port+tls 443 $ACME/$DOM.{crt,key} |> ndex index.php index.html @ports+fcgi @ports |> fcgi unix:///run/php-fpm.sock *.php $DOM @ports+fcgi |> root $WWW show <sub>.$DOM @ports+fcgi |> root $WWW/$sub app.$DOM @ports |> rprx http://127.0.0.1:8080 qwa.su |> port 80 |> port+tls 443 $ACME/qwa.su.{crt,key} |> rdir 307 https://qwaderton.org/ We use this configuration on our web server (as it is at the time of writing this manual). If you need something simplier to start quickly, use this config: DOM=mydomain TLS=/etc/acme/$DOM WWW=/srv/www/$DOM $DOM |> port 80 |> port+tls 443 $TLS.{crt,key} |> root $WWW show # CAVEATS 1. ICF does not support strings with spaces - there are **no quotes or escapes**. Every character except space is treated as part of a token, including `!`, `*`, `/`, and so on. Glob patterns passed to **fcgi** are forwarded as-is to the server. 2. Variables are evaluated top-down at the point of declaration. Forward references do not work: if `B=$A` appears before `A=value`, `B` will contain the literal `$A`. 3. Only one pair of curly braces is expanded per argument. `file.{a,b}.{x,y}` does not produce four arguments - only the first `{}` pair is expanded. 4. A directive outside of any block (a `|>` line before the first block header) is a parse error and will prevent the server from starting. 5. Duplicate ports across blocks are silently deduplicated - the first declaration wins. TLS certificate paths from a later block with the same port are ignored. # SEE ALSO **d2o**(1)
