glu
Markdown mode

Markdown mode

glu can convert Markdown files directly to PDF. Instead of writing Lua scripts, you write Markdown and glu handles the typesetting automatically.

glu document.md

This produces document.pdf. glu detects the mode by the file extension: .lua for Lua scripts, .md for Markdown.

For complete worked examples (a DIN 5008 letter, a ZUGFeRD invoice, letterheads, headers and footers, charts), see the How-tos.

YAML frontmatter

A YAML block at the beginning of the file sets document metadata:

---
title: My Document
author: Jane Doe
css: custom.css
papersize: a4
format: PDF/UA
lang: en
---
Field Description
title PDF document title
author PDF author field
css Additional CSS file to load, relative to the document
papersize Page size (a4, letter, 20cm 20cm, etc.)
format PDF conformance level: PDF/UA / PDF/UA-1 (tagged PDF, ISO 14289-1), PDF/UA-2 (PDF 2.0, ISO 14289-2) or PDF/A-3b
lang Document language (e.g. de, en)
math Enable TeX math ($…$, $$…$$). Off by default. See Mathematics (TeX).
highlight-style Syntax highlighting style (default: github)
extensions Switch optional Markdown extensions on or off. See Optional Markdown extensions.
attachments List of files to embed in the PDF. See Attachments.

All frontmatter fields (including custom ones) are available from Lua as the _frontmatter global table:

-- Access standard fields
local title = _frontmatter.title

-- Access custom fields
local version = _frontmatter.version

This means you can add arbitrary key-value pairs to the frontmatter and read them from Lua blocks or the companion Lua file.

Companion Lua file

If a Lua file with the same base name exists (e.g., report.lua for report.md), it is loaded automatically before the Markdown is processed. This is the place for function definitions, variable initialization, and callback registration.

report.md      ← your document
report.lua     ← loaded automatically (if present)

Example report.lua:

local frontend = require("glu.frontend")

function currency(amount)
    return string.format("%.2f €", amount)
end

total = 42 * 3

The functions and variables are available in the Markdown file via Lua blocks and inline expressions.

Lua blocks

Fenced code blocks with {lua} are executed and removed from the output:

```{lua}
result = math.sqrt(144)
```

Lua blocks run in the same Lua state as the companion file. They execute in document order, so later blocks can use variables from earlier ones.

Returning text

If a Lua block uses return, the returned string is inserted into the Markdown source at the position of the block:

```{lua}
return '<div class="note">'
```

This paragraph is wrapped in a div.

```{lua}
return '</div>'
```

This is useful for injecting HTML tags, dynamic content, or computed Markdown.

Recording and processing text

Two Lua blocks can work together to capture and transform a section of text. startrecording() begins capturing all text between Lua blocks; stoprecording() stops capturing and returns the captured text as a Lua string:

```{lua}
startrecording()
```

This text will be captured and processed.

```{lua}
local content = stoprecording()
return string.upper(content)
```

The result: “THIS TEXT WILL BE CAPTURED AND PROCESSED.” — the Markdown between the two blocks was captured, transformed by string.upper(), and re-inserted via return.

Recording works on the raw Markdown text level (before goldmark converts to HTML). This means:

  • Markdown formatting (**bold**, # heading) inside the recorded text is preserved and processed normally after the Lua transformation
  • {= expr =} inline expressions inside recorded text are expanded after the Lua transformation — if the transformation alters the {= ... =} syntax, the expressions will break
Function Description
startrecording() Begins capturing text. Subsequent text goes to the buffer instead of the output.
stoprecording() Stops capturing and returns the captured text as a string.

Display-only blocks

To show Lua code without executing it, use {!lua} instead of {lua}. The block is rendered with syntax highlighting but not executed:

```{!lua}
-- This code is displayed, not executed
items = { "one", "two", "three" }
return #items
```

This is useful for documentation or slides where you want to show Lua examples.

Showing fenced code blocks verbatim

To display a ```{lua} block literally (including the backtick fencing), wrap it with more backticks. Use 5 backticks to protect the inner 3-backtick block from execution:

`````
```{lua}
return "This is shown as code, not executed"
```
`````

The outer fence must have strictly more backticks than any inner fence. This works for any number of nesting levels — wrap with 6 backticks to show 5 backticks, and so on.

Inline expressions

Use {= expr =} to insert the result of a Lua expression:

The total is {= total =} and the square root of 144 is {= math.sqrt(144) =}.

Any Lua expression that returns a value works — variables, function calls, arithmetic:

The price is {= currency(19.99) =}.

Markdown features

glu uses goldmark with these extensions:

  • Tables — pipe tables with alignment
  • Strikethrough~~deleted~~
  • Autolinks — URLs are automatically linked
  • Syntax highlighting — fenced code blocks with a language tag are highlighted using chroma
  • Block attributes{.class #id key="value"} on headings and after blocks, see below

Further pandoc-style extensions (fenced divs, bracketed spans, footnotes, definition lists, smart punctuation, automatic heading identifiers) are switched through the frontmatter, see Optional Markdown extensions.

Standard Markdown features: headings, bold, italic, code, lists, blockquotes, horizontal rules, and links.

Raw HTML is supported inside Markdown. This allows using CSS features like leaders for table-of-contents dot fills via inline <span> elements, or generating barcodes with <barcode> elements.

CSS classes and attributes on blocks

Two syntaxes attach HTML attributes (classes, ids, key/value pairs) to the generated elements, so custom CSS can target individual blocks. ATX headings take an attribute suffix on the same line:

# Title {.right #main}

renders as <h1 class="right" id="main">. Every other block type (paragraphs, blockquotes, lists, code fences, tables) takes an attribute line directly after the block, with no blank line in between (kramdown style):

Lorem ipsum dolor sit amet, consetetur sadipscing elitr,
sed diam nonumy eirmod tempor invidunt ut labore.
{.three}

renders as <p class="three">. Together with a custom stylesheet this enables per-paragraph styling that plain Markdown has no syntax for, e.g. a drop cap:

p.three { initial-letter: 3; }

An attribute line separated from the preceding block by a blank line attaches to nothing and is dropped from the output. For attributes on inline text, use bracketed spans; for attributes on a group of blocks, use fenced divs.

Optional Markdown extensions

The extensions: frontmatter key switches optional Markdown syntax on or off. It accepts extension names in pandoc spelling, either comma-separated or as a YAML list; a leading - disables a default-enabled extension:

---
extensions: smart, auto_identifiers, -footnotes
---
Extension Default Description
fenced_divs on ::: {.class} wraps blocks in a div
bracketed_spans on [text]{.class} attaches attributes to inline text
footnotes on [^1] footnote references and definitions
definition_lists on : definition lines build dl/dt/dd
smart off typographic quotes, dashes, ellipsis
auto_identifiers off automatic id attributes on headings
tex_math_dollars off TeX math, same as math: true. See Mathematics (TeX).

The defaults are chosen so existing documents render unchanged: the default-on extensions use syntax that plain documents cannot contain, while smart rewrites regular text and auto_identifiers changes the document’s link targets, so both are opt-in.

Fenced divs

A line of at least three colons plus attributes opens a block container, a line of colons alone closes it. This wraps any number of blocks in a <div> without writing raw HTML, for styling with custom CSS or for switching the language of a whole section:

::: {.note lang=en}
This section is in English and hyphenates accordingly.

It can contain multiple blocks, lists, tables and nested divs.
:::

A single bare word is a class shorthand: ::: warning is the same as ::: {.warning}. Divs nest; each closing fence closes the innermost open div.

A lang= attribute switches the hyphenation language for the div’s content, and in PDF/UA mode the switch is also declared in the structure tree (/Lang on the element), so screen readers pronounce the section in the right language.

Bracketed spans

Bracketed text immediately followed by attributes becomes a <span> with those attributes:

A paragraph with [ein deutscher Einschub]{lang=de} in the middle,
or [specially styled text]{.spaced} for a CSS class.

This is the inline counterpart to block attributes: language switches, custom classes and ids mid-paragraph without raw HTML. Links, emphasis and other inline markup work inside the brackets.

Footnotes

Some text with a footnote.[^1]

[^1]: The footnote text.

The notes are collected in an endnote block at the end of the document, set in a smaller size and separated by a rule. The web-style backlink is hidden by the default stylesheet; bring it back with .footnote-backref { display: inline; }.

Definition lists

Term
: The definition of the term.

Another term
: Another definition.

renders as <dl>/<dt>/<dd>, styled with a hanging indent by the default stylesheet.

Smart punctuation

With smart enabled, straight quotes become typographic quotes, -- becomes an en dash, --- an em dash and ... an ellipsis. The quote style follows the document language: with lang: de (or any de-* tag) German quotation marks („…“ and ‚…‘) are used, any other language gets English ones (“…” and ‘…’).

Automatic heading identifiers

With auto_identifiers enabled, every heading without an explicit {#id} attribute gets an id derived from its text, following pandoc’s algorithm: letters are lowercased, spaces become hyphens, punctuation is dropped and non-ASCII letters survive (## Über uns becomes über-uns). Duplicate ids get -1, -2 suffixes. This enables internal links to headings without writing any attributes:

See [the introduction](#introduction).

## Introduction

Syntax highlighting

Fenced code blocks with a language identifier are automatically syntax-highlighted:

```go
func main() {
    fmt.Println("Hello")
}
```

The highlight style can be set in the frontmatter:

---
highlight-style: monokai
---

The default style is github. Any chroma style can be used (e.g. monokai, dracula, solarized-dark, tango).

Internal links

HTML elements with an id attribute create PDF destinations. Use <a href="#..."> to link to them:

<h2 id="introduction">Introduction</h2>

Some text...

Go back to <a href="#introduction">Introduction</a>.

The id attribute works on any block-level HTML element. The href must start with # followed by the target id.

Default stylesheet

Markdown mode includes a built-in CSS stylesheet that provides sensible defaults:

  • A4 page size with 2cm margins
  • Serif font (CrimsonPro) for body text at 10pt
  • Scaled heading sizes (h1: 24pt down to h6: 10pt italic)
  • Monospace font (CamingoCode) for code and pre blocks
  • Table styling with header borders
  • Blockquote indentation and italic style

The defaults can be overridden with a custom CSS file.

Custom CSS

Additional CSS can be loaded in two ways:

Via frontmatter:

---
css: style.css
---

Via command line:

glu --css style.css document.md

The custom CSS is applied after the default stylesheet, so it overrides matching rules.

A relative path in the css: frontmatter field resolves against the directory of the Markdown file, like a <link href> in HTML: css: style.css always finds the stylesheet next to the document, no matter where glu is invoked from. The --css command-line flag resolves against the working directory, as command-line arguments usually do.

Mathematics (TeX)

Set math: true in the frontmatter to write formulas with TeX dollar syntax. Inline math goes between single dollars, display math between double dollars. The TeX is converted to MathML and typeset by the same OpenType-MATH engine that powers the <math> HTML reader, so it needs no Pandoc, no JavaScript and no external converter.

---
title: TeX math in Markdown
lang: en
css: math.css
math: true
---

The mass-energy equivalence `$E=mc^2$` sits inline with the prose.

The quadratic formula as a display equation:

`$$x=\frac{-b\pm\sqrt{b^2-4ac}}{2a}$$`

(The backticks above are only there so this page does not itself parse the example as math. In your document write the dollars directly: $E=mc^2$ and $$…$$ on their own.)

Math parsing is off by default so that a $ used as a currency sign is never reinterpreted. It is a real Markdown inline parser, not a search and replace, so dollars inside `code spans` and fenced code blocks are left untouched.

A math font is required

As with the HTML MathML reader, the engine needs a font carrying an OpenType MATH table (Latin Modern Math, STIX Two Math, XITS Math, Cambria Math, …). Point the generated <math> element at it through CSS, for example in the css: file:

@font-face {
  font-family: "Latin Modern Math";
  src: url("latinmodern-math.otf");
}

/* The <math> element generated from the dollars looks up font-family on
   itself, so the engine can find the MATH-table font. */
math {
  font-family: "Latin Modern Math";
  font-size: 12pt;
}

Predictable dollars (Pandoc/PLOS rule)

To keep prose with stray dollars safe, opening and closing delimiters follow the same strict rule Pandoc uses and PLOS expects for Markdown submissions:

  • The opening $ must not be followed by whitespace.
  • The closing $ must not be preceded by whitespace.
  • The closing $ must not be directly followed by a digit.

So it costs $5 and $10 stays plain text, while $x+1$ is a formula. Display math ($$…$$) is matched on a single line.

Supported TeX subset

This is a focused subset aimed at typical prose mathematics, not a full LaTeX engine:

Construct Examples
Variables, digits, operators a, 42, 3.14, + - = < >
Superscript / subscript x^2, a_i, x_i^2 (combine to limits/scripts)
Fractions \frac{a}{b}, \frac12
Roots \sqrt{x}, \sqrt[3]{x}
Greek letters \alpha, \beta, \Sigma, \omega, …
Relations / operators \leq, \geq, \neq, \approx, \pm, \times, \cdot, …
Arrows \to, \rightarrow, \Rightarrow, \mapsto
Big operators \sum, \prod, \int: sums and friends stack their limits above/below in display style, integrals keep side scripts (TeX nolimits convention); inline everything is a script pair
Named operators \sin, \cos, \log, \lim, \limsup, \max, …
Primes f', f'', f''' (set as ord atoms after the base, since OpenType math primes are designed pre-raised); f'^2 attaches the superscript after the marks
Stretchy fences \left( … \right), \left\lfloor … \right\rfloor, invisible \left.; delimiters grow with the content via font variants and glyph assemblies
Delimiters \lfloor, \rfloor, \lceil, \rceil, \langle, \rangle, \vert, \Vert, \lbrace, \rbrace
Upright text \mathrm{...}, \text{...}

Grouping with { … } works everywhere. An unknown command degrades to its name as an upright identifier rather than failing the render, so one stray macro never aborts a document.

Not yet covered: multi-line display blocks, matrices and \begin{} environments, and accents such as \hat or \vec. The \(…\) and \[…\] delimiters are not recognised; use the dollar forms.

Accessibility

Because the formula becomes MathML, it rides the full accessibility path automatically. Under PDF/UA-2 each formula is wrapped in a Formula structure element with a text /Alt, and the MathML source is embedded as an associated file (application/mathml+xml, AFRelationship /Supplement) so assistive technology can read the semantics. See Tagged PDF (PDF/UA) and verify with pdfa11y or veraPDF.

Tagged PDF (PDF/UA)

Setting format: PDF/UA (or the explicit aliases PDF/UA-1 and PDF/UA-2) in the frontmatter produces a tagged, accessible PDF that conforms to the chosen PDF/UA revision:

---
title: Accessible Report
author: Jane Doe
format: PDF/UA
lang: en
---
format value Standard PDF version XMP pdfuaid:part
PDF/UA or PDF/UA-1 ISO 14289-1 1.7 1
PDF/UA-2 ISO 14289-2 2.0 2 (plus pdfuaid:rev = 2024)

When PDF/UA is active, glu automatically:

  • Creates a document structure tree with roles derived from HTML elements (P, H1–H6, Table, L/LI, Figure, etc.)
  • Tags table cells as TH/TD with proper THead/TBody grouping and scope attributes
  • Marks page headers and footers as pagination artifacts
  • Generates PDF bookmarks from headings (under UA-2 these point to structure destinations, ISO 14289-2 §8.8)
  • Sets the document language from the lang field
  • Declares language switches in the structure tree: a block element whose lang= attribute differs from the inherited language gets /Lang on its structure element (e.g. a fenced div with lang=en in a German document), and its children inherit it. Inline switches (<span lang>, bracketed spans) currently affect hyphenation only, not the structure tree.

Under PDF/UA-2 the structure tree additionally uses the HTML5 namespace for HTML-equivalent roles (lowercase p, h1h6, figure, table, …) with a RoleMapNS mapping each one to the corresponding PDF 2.0 Standard Structure Namespace role. Document-level and PDF-specific roles without HTML5 equivalents (Document, Note, LBody, Lbl, L) stay in the PDF 2.0 SSN.

No additional Lua code or CSS is needed — the Markdown structure maps directly to the PDF structure tree.

The lang field sets the document’s default language tag (BCP 47). This is required for PDF/UA conformance and used by screen readers to select the correct pronunciation.

veraPDF is the canonical conformance checker:

verapdf --flavour ua1 report.pdf   # for PDF/UA / PDF/UA-1
verapdf --flavour ua2 report.pdf   # for PDF/UA-2

Attachments

The attachments: frontmatter key embeds arbitrary files in the PDF — supplementary specs, raw data, source CSVs, anything PDF viewers should expose in their attachment pane:

---
attachments:
  - file: spec.pdf
    name: Technical specification.pdf
    description: source for the rendered tables
  - file: data.csv
    mimetype: text/csv
---
Field Required Default
file yes
name no basename of file
description no empty
mimetype no application/octet-stream

Paths are resolved relative to the Markdown source file. Each attached file is written with /AFRelationship /Alternative and registered in the document’s name tree, so pdfdetach -list and any PDF viewer’s attachment pane pick them up.

Compliance formats (ZUGFeRD, XRechnung, PEPPOL, …)

glu does not ship special frontmatter for compliance formats. The pattern is companion Lua + generic Markdown frontmatter: set format: PDF/A-3b (or whatever the standard requires) in the frontmatter, then do the format-specific plumbing — XML attachment, XMP extension schema, output-intent ICC, data extraction — in <stem>.lua next to the Markdown file.

This keeps glu’s core focused on typesetting, lets compliance-format requirements evolve at their own pace, and gives the author full control over what gets attached, in what order, with which metadata.

The boxesandglue-examples/glu/markdown/zugferd-invoice/ example shows the pattern end-to-end for ZUGFeRD/Factur-X:

  • rechnung.md — visible invoice with format: PDF/A-3b and {= zugferd.* =} placeholders
  • rechnung.lua — auto-loaded companion: parses invoice.xml via cxpath into a flat zugferd Lua global, registers a page_init callback that calls doc:load_colorprofile(...), doc:attach_file{name="factur-x.xml", ...} and doc:add_xmp_extension{...} once per render
  • invoice.xml — CII XML
  • AdobeRGB1998.icc — output-intent profile

Adapting the same pattern to XRechnung, PEPPOL, BSI TR-RESISCAN or any other domain-specific compliance format is a matter of editing the companion Lua — no glu code changes, no glu release dependency.

Verifying ZUGFeRD output

pdfdetach -list rechnung.pdf
# 1 embedded files
# 1: factur-x.xml

exiftool -XMP-pdfaid:Part -XMP-zf:ConformanceLevel \
         -XMP-zf:DocumentFileName rechnung.pdf
# Part                : 3
# Conformance Level   : EN 16931
# Document File Name  : factur-x.xml

veraPDF can validate the PDF/A-3b side:

verapdf --flavour 3b rechnung.pdf

CSS reference

You can use @page to change the page size and margins:

@page {
    size: letter;
    margin: 1in;
}

body {
    font-size: 11pt;
    line-height: 1.5;
}

h1 {
    color: #333;
}

Page margin boxes and counters

CSS @page rules support margin boxes for headers and footers (see also the headers and footers how-to). Inside margin boxes, use the content property with counter() to display page numbers:

@page {
    size: a5;

    @bottom-center {
        content: "Page " counter(page) " of " counter(pages);
        font-size: 9pt;
        color: #666;
    }
}

Available counters:

Counter Description
page Current page number (1-based)
pages Total number of pages

The content property can mix literal strings and counter values freely: content: counter(page) " / " counter(pages);

Images can be placed in margin boxes using url():

@page {
    @top-right {
        content: url("logo.pdf");
    }
}

The image is scaled proportionally to fit the margin box height and aligned according to the box position (left, center, or right).

Use @page :first to override margin boxes on the first page:

@page :first {
    @top-right {
        content: "";  /* no logo on first page */
    }
}

Available margin boxes: top-left-corner, top-left, top-center, top-right, top-right-corner, bottom-left-corner, bottom-left, bottom-center, bottom-right, bottom-right-corner.

Auxiliary file

Forward references like counter(pages) (the total page count) and the table of contents cannot be resolved on the first typesetting pass — they depend on values that aren’t known until the document is fully laid out. glu solves this with an auxiliary file and an automatic re-run loop:

For a source file document.md (or --output FILE if set), glu writes <stem>-aux.json — a formatted JSON file next to the PDF.

  1. Pass 1 — glu typesets the document. counter(pages) shows 0 because no previous data exists. After the pass, glu writes the actual page count and collected headings to the aux file.
  2. Pass 2 — glu reads the new aux file, starts with a fresh Lua state, and typesets again. The forward references now resolve to real values.
  3. Convergence — when a pass writes an aux file that is byte-identical to what it read, glu stops.

The loop is bounded by --max-passes (default 3). If the aux file oscillates between two different states across passes — typically a sign of non-deterministic Lua code — glu aborts the loop early with exit code 6 instead of wasting more passes. --max-passes=1 disables auto-rerun entirely.

Each pass runs with a fresh Lua state: companion .lua is re-loaded, {lua} blocks are re-run, lifecycle callbacks fire again. This is deliberate — accumulating state across passes would compound side effects (counters, registered callbacks, file writes).

Use --clean to delete the aux file before pass 1:

glu --clean document.md

Cross-references

The two-pass loop is what makes CSS target-counter(attr(href), page), target-counters(attr(href), chapter, ".") and target-text(attr(href)) resolve to real page numbers, section numbers and titles in Markdown documents. The first pass renders the cross-references as ? and collects {id → {page, text, counters}} for every anchor; the second pass reads the aux entry back and emits final values. See the cross-references reference for the CSS shape, and these two example folders for end-to-end Markdown setups:

JSON format

The aux file uses _-prefixed keys for system data. User data can use any key without underscore prefix:

{
  "_headings": [
    { "level": "h1", "text": "Introduction", "page": 1 },
    { "level": "h2", "text": "Summary", "page": 3 }
  ],
  "_pages": 3,
  "my_index": ["Alpha", "Beta"]
}
Key Type Description
_pages number Total page count (set by glu after each run)
_headings array Heading entries with level, text, and page
user keys any Custom data stored by Lua code

Accessing aux data from Lua

The aux file is exposed as the _aux global table in Lua. It is readable and writable — any changes are saved back to the JSON file after the run.

-- Read system data from previous run
if _aux._pages then
    print("Last run: " .. _aux._pages .. " pages")
end

-- Read headings (also available as _toc for backward compatibility)
if _toc then
    for _, h in ipairs(_toc) do
        print(h.level .. ": " .. h.text .. " (page " .. h.page .. ")")
    end
end

-- Store custom data that persists across runs
_aux.build_count = (_aux.build_count or 0) + 1
_aux.my_keywords = { "typesetting", "PDF", "Lua" }

The _toc global is an alias for _aux._headings and remains available for backward compatibility.

System keys (with _ prefix) are updated by glu after each run. You can read and even overwrite them, but glu will set _pages and _headings to the actual values at the end of the run. To store persistent custom data, use keys without underscore prefix.

The lifecycle callbacks document_start, content_ready, and document_end provide precise control over when _aux is read or modified. Modifications made in document_end are included when the aux file is written.

Go templates

The --template flag enables Go template expansion before Markdown processing:

glu --template document.md

This processes the file through Go’s text/template engine first, allowing template directives like {{.Variable}} and {{range}} loops.

How it works

The Markdown-to-PDF pipeline has several stages:

Markdown-to-PDF pipeline

Each stage is optional: a plain Markdown file without any Lua or frontmatter works fine.

Debug modes

Two debug flags let you inspect intermediate stages of the pipeline:

--markdown — prints the expanded Markdown (after Lua blocks and inline expressions, before goldmark) to stdout:

glu --markdown document.md

--html — prints the generated HTML (after goldmark conversion, before PDF rendering) to stdout:

glu --html document.md

Both flags suppress PDF generation and write to stdout instead. Use them together with shell redirection to save the output:

glu --html document.md > output.html

Complete example

report.md:

---
title: Monthly Report
author: Jane Doe
---

# Monthly Report

This report covers the period from January to March.

| Month   | Revenue  | Expenses |
|---------|----------|----------|
| January | {= currency(15420) =} | {= currency(12300) =} |
| February| {= currency(18900) =} | {= currency(14100) =} |
| March   | {= currency(21050) =} | {= currency(15800) =} |

**Total revenue:** {= currency(15420 + 18900 + 21050) =}

---

> Report generated with glu.

report.lua:

function currency(amount)
    return string.format("%.2f €", amount)
end

Run:

glu report.md    # → report.pdf

Command line reference

See Using glu for the full list of options and subcommands. The flags that matter most for Markdown mode are:

Flag Purpose
--template Apply Go text/template expansion before frontmatter extraction.
--css FILE Load an additional CSS file after the default stylesheet.
--markdown Print expanded Markdown to stdout (debug, no PDF).
--html Print generated HTML to stdout (debug, no PDF).
--clean Delete the aux file before the first pass.
--max-passes N Cap the auto-rerun loop (default 3).