Records
Your pages are your database: each page declares its own entry in one or more collections as a record — a small block of typed fields that other pages pull into listings and counts. A blog post might declare its record like this:
<!-- localhoster:record collection="blog" -->
<!-- localhoster:record-field name="title" type="text"
```
Drop a folder, get an HTTPS site
``` -->
<!-- localhoster:record-field name="date" type="text" value="2026-06-20" -->
<!-- localhoster:record-field name="reading-minutes" type="integer" value="4" -->
<!-- localhoster:record-field name="featured" type="boolean" value="true" -->
<!-- localhoster:record-field name="summary" type="html"
```
<p>Name a folder for its domain, served over HTTPS.</p>
``` -->
<!-- localhoster:/record -->
Record
A page can be part of any number of collections, and can hold more than one record for
the same collection, so one page can contribute several entries to a list. On each page that should appear
in lists, open a record block naming its collection:
| Parameter | Description |
|---|---|
collection | the collection this record joins; other pages list and count by it |
Fields
Inside the record, declare each field with a record-field: a name, an optional
type, and a value given by either value="…" (kept verbatim, spaces and all) or a fenced
``` block (trimmed):
| Parameter | Description |
|---|---|
name | the field name — used as {{name}} and in filters, sorts and groups |
type | one of the types below (default auto) |
value | the value, kept verbatim; omit it to use a fenced ``` block instead, which is trimmed |
Field Types
A field’s type decides two things: how it is compared by filters, sorts and
groups (so numbers behave as numbers, not text where "10" < "9"), and how its value is
written into a template. Every value is HTML-escaped so a field’s text can never inject
markup — except html, which is inserted raw, for fields that hold real markup:
| Type | Description |
|---|---|
text | plain text, HTML-escaped on output; compared exactly and lexicographically |
html | inserted raw, not escaped, for values that are real HTML markup; compared as text |
integer | whole numbers; numeric comparison and equality |
number | numbers with decimals; numeric comparison |
boolean | true / yes / 1 / on are true, anything else false |
auto | untyped (the default): numeric where the value looks numeric, otherwise text |
integer, say) is reported with its page and field, and the site is left unchanged until it is fixed.Fields are invisible
The values live only in the comments, never wrapped around page markup, so a record is not limited to what the page shows: a field can be a date, a sort key, a thumbnail URL or a summary that appears nowhere on the page itself. The record’s own path is its key.
Errors
Every record is validated before the site is written; a problem is reported against the page and the field, and the site is left unchanged until it is fixed (other sites still run):
| Message | When it appears |
|---|---|
record "…" may only contain record-field (found …) | a record block holds some other comment |
record "…": a record-field needs name="…" | a record-field with no name |
record-field "…": type="…" is not one of text, html, integer, number, boolean, auto | an unknown type |
record-field "…": type="integer" but value "…" is not a whole number | an integer value that is not a whole number |
record-field "…": type="number" but value "…" is not a number | a number value that is not numeric |
record-field "…": type="boolean" but value "…" is not a boolean (true/false) | a boolean value that is not true/false |
record-field "…": missing a ``` value block | neither value="…" nor a fenced block to read the value from |