KATA
A human-friendly format for modeling structured data
KATA (“Key Annotated Tree of Attributes”) is a human-friendly format for modeling structured data that is used throughout Cerb (since 9.6) to describe configurations, customizations, sheets, and automations.
KATA was inspired by YAML1 but avoids many of its pitfalls.
Pitfall | YAML | KATA |
---|---|---|
Data types | YAML attempts to automatically detect data types, which requires the use of double quotes on keys and values to ensure they remain text. For example, no becomes FALSE and currency/versions (1.50 ) become decimal (1.5 ). |
KATA treats all keys and values as text without any type coercion. You never need to use quotes. You can optionally annotate a key to convert its value to a specific type. |
Local objects | YAML allows language-specific tags. In some implementations (PHP, Perl, Python, Ruby) these may be automatically enabled for classes and present a security issue. | KATA never executes or refers to outside code. |
Curly braces | YAML allows objects to be defined on a single line using {key:value} syntax. This conflicts with our {{placeholder}} syntax, and requires double quoting, explicit types, or text blocks. |
KATA doesn’t interpret {} , so those characters can be used in values without quoting. |
Text blocks | YAML has a variety of symbols for blocks of text (| , |- , > ). The default always preserves the first trailing linefeed and trims the rest. |
KATA implies blocks of text by key annotation (e.g. @text ). The first trailing linefeed is always ignored and the rest are preserved. |
Lists | YAML requires - prefixes on list items and is sensitive to indentation. Lists are required for duplicate key names amongst siblings. Mistakes with lists of objects can lead to children being added to the wrong parent. |
KATA has @csv and @list key annotations for text-based lists. Sibling keys can be repeated with unique identifiers and never require list syntax. |
Comments | YAML ignores any text following an unquoted # character. |
KATA only treats indented lines that begin with # as a comment. You do not need to escape the character in values. |
Syntax
Indentation
KATA uses indentation (spaces) to build a tree of key/value relationships.
Key names end with a colon (:
), which may be followed by either a value, or a set of child keys at a deeper level of indentation.
By convention, the indentation for each level should be two spaces.
parent:
child:
name:
Root
The root of the tree is implicit, so there may be multiple keys at the top-level:
picklist:
date_range:
text:
Key names
Key names must be unique amongst their siblings, but can be repeated elsewhere.
Key names serve as declarative instructions to a feature using KATA for customization (e.g. dashboard filters, snippet prompts, form interactions, automations). In such cases, the possible keys are predefined by that feature.
A slash (/
) may be appended to a key name to provide a unique identifier. This format should be read as type/name:
.
options:
picklist/status:
picklist/color:
picklist/group:
Values
A key may be followed by a text value rather than children. KATA will not automatically detect its type, which often removes the need for escaping.
object:
color: red
Values may also contain subsequent colons (:
), which do not require escaping:
widget:
label: Status:
Whitespace
Sibling keys may be separated with blank lines for readability. The blank lines must contain the same indentation as the keys.
widget/chart:
type: chart
label: Chart
data: ...
widget/gauge:
type: gauge
label: Gauge
data: ...
Key annotations
KATA does not perform type coercion on key values. It will not unpredictably convert digits to numbers, nor words like true
or no
to booleans. All values are treated as text by default.
To manipulate values, a comma-separated list of annotations may be appended to a key name starting with a @
character. The annotations are processed in order.
picklist:
options:
color@csv: red, green, blue
multiple@bool: no
hidden@bool:
{% if has_access %}
no
{% else %}
yes
{% endif %}
Text blocks
When using key annotations, a value may contain multiple lines of text. Most annotations imply a text block (e.g. @bool
, @csv
, @json
, @list
). The @text
annotation may be used for arbitrary text without any special handling.
comment:
content@text:
This is a comment with
multiple lines of content.
author:
name: Cerb
Comments
Lines that begin with #
are treated as comments and are ignored.
picklist:
# The options from a placeholder
options:
color@csv: {{colors}}
You do not need to escape the #
character in values or text blocks.
# This is a comment
article:
title: Using #commands <-- not a comment
format: markdown
content@text:
# Heading <-- not a comment
Some **bold** text.
References
You can break up a complex tree into manageable, reusable sections with references.
You define a reference by prefixing an ampersand (&
) to a top-level key.
Any key can then use an @ref
annotation to copy the contents and annotations of the reference.
picklist:
options@ref: colors
&colors@list:
red
green
blue
An @ref
may target a child of a reference using dot-notation:
picklist:
options@ref: options.colors
&options:
colors@list:
red
green
blue
And references themselves may contain @ref
annotations:
picklist:
options@ref: options.colors
&options:
colors@ref: colors
&colors@list:
red
green
blue
A reference’s annotations take the place of @ref
, and any remaining annotations will affect the copied content.
For instance, this copies a reference as a @text
block and then converts it to a @list
:
picklist:
options@ref,list: colors
&colors@text:
red
green
blue
The result of all four examples above is:
picklist:
options@list:
red
green
blue
Dictionaries
Features that use KATA may enable scripting and provide a dictionary of placeholders for dynamic content.
Placeholders and scripting may be used in any value and do not require escaping.
Use the @raw annotation to prevent tags from being parsed.
chooser:
label: {{label}}
params:
record_type: {{record_type}}
Annotation Reference
base64
@base64
converts a key’s value from base64-encoded2 text into binary. This is particularly useful for HMAC keys and images.
image@base64: QnVzdGVkISBUaGlzIGlzIG5vdCByZWFsbHkgYW4gaW1hZ2Uu
bit
@bit
convert’s a key’s value into a 0
or 1
.
The following values result in 0
:
- null/blank
0
false
off
no
n
result@bit: off
Any other value returns a 1
.
bool
@bool
convert’s a key’s value into a boolean true
or false
.
The following values result in false
:
- null/blank
0
false
off
no
n
Any other value returns true
.
enabled@bool: yes
csv
@csv
converts a comma-separated list into an array.
colors@csv: red,green,blue
date
@date
converts a human-readable absolute (Jan 1 2025 08:00
) or relative (+2 hours
) date text value into a Unix timestamp.
when@date: +2 hours
int
@int
parses the key’s value as a whole number.
number@int: 123
json
@json
parses text as a JSON-encoded value.
This is particularly useful when combined with a placeholder based on an API response.
person@json:
{
"name": "Kina Halpue",
"title": "Customer Support Manager"
}
numbers@json: [1,2,3]
key
@key
sets the value from a dictionary path.
http_status@key: response.http.status.code
list
@list
converts text into an array with one line per item.
colors@list:
red
green
blue
raw
@raw
returns a key’s text without substituting {{placeholders}}
or executing bot scripts using the dictionary.
This is useful for returning templates to other functionality (e.g. sheets).
template@raw:
{{person}} is {{title}} at {{organization}}
ref
@ref
copies the contents of a top-level &key:
into the current key.
This can target a reference by name (key@ref: target
) or by path (key@ref: target.nested.child.value
).
The annotations of the target key replace the @ref
, and any remaining annotations apply to the copied content.
See: References
event/start@ref: menu
&menu:
options@list:
Option 1
Option 2
Option 3
text
@text
reads the key’s indented value as a text block. This continues until the indent returns to the same level as the key.
This is useful when a text block can’t be implied from any other attributes.
content@text:
This is a _bunch_ of content
on several lines
that stops **here**
format: markdown
The first trailing linefeed is always removed so that multiple line scripts can return a single value.
End the text block with one or more indented blank lines to keep linefeeds:
content@text:
This content ends
with a blank line
format: markdown
trim
@trim
removes leading and trailing whitespace from a key’s value.
content@trim: this has no whitespace
Footnotes
-
YAML: https://yaml.org ↩