Fact-Based Targeting
Fact-based targeting matches peels based on their reported facts rather than their peel ID. Expressions use the G@ prefix for grains (facts) or I@ as an alias with identical syntax and matching behavior.
Syntax
G@<key>:<value>
G@<key>:<operator><value>The general form is G@key:value for equality matching, or G@key:<op>value
for comparison operators. Nested keys use dot notation.
Prefixes
| Prefix | Data Source | Example |
|---|---|---|
G@ | Peel facts (grains) | G@os:ubuntu |
I@ | Alias of fact matching (same source as G@) | I@role:webserver |
Both prefixes use the same FactMatcher implementation and currently resolve
against the facts dataset.
Dot Notation
Nested fact values are accessed using dot-separated keys. Zester traverses the nested map structure one level per dot segment.
Given a peel with these facts:
{
"os": {
"family": "debian",
"release": "22.04",
"codename": "jammy"
},
"cpu_count": 8,
"memory_gb": 32,
"roles": ["web", "api"]
}You can target using:
| Expression | Matches? | Reason |
|---|---|---|
G@os.family:debian | Yes | Nested lookup: os -> family -> "debian" |
G@os.release:22.04 | Yes | String comparison |
G@cpu_count:8 | Yes | Numeric equality |
G@os:ubuntu | No | os is a map, not a string |
Comparison Operators
The value portion of a fact expression can begin with a comparison operator. If no operator prefix is present, equality is assumed.
| Operator | Description | Example |
|---|---|---|
| (none) | Equal (case-insensitive) | G@os:ubuntu |
!= | Not equal (case-insensitive) | G@os:!=windows |
> | Greater than | G@cpu_count:>4 |
>= | Greater than or equal | G@cpu_count:>=4 |
< | Less than | G@memory_gb:<16 |
<= | Less than or equal | G@memory_gb:<=64 |
Operator parsing order
Operators are parsed in this order to handle multi-character operators correctly:
>=(before>)<=(before<)!=><- (default: equality)
Numeric vs string comparison
When both the actual fact value and the expected value can be parsed as floating-point numbers, Zester performs numeric comparison:
# Numeric: 8 >= 4 -> true
zester 'G@cpu_count:>=4' state.apply performance.tuneWhen either value cannot be parsed as a number, Zester falls back to lexicographic (string) comparison:
# String comparison: "22.04" > "20.04" -> true (lexicographic)
zester 'G@os.release:>20.04' state.apply upgrade.checkString comparison gotcha
Lexicographic comparison works well for zero-padded version strings
(20.04 vs 22.04) but can produce unexpected results with unpadded
numbers as strings. For example, "9" > "10" is true in lexicographic
order. Always ensure numeric facts are stored as actual numbers, not
strings.
Equality is case-insensitive
Equality and not-equal (!=) comparisons use case-insensitive matching
via strings.EqualFold:
# All of these match a peel with os="Ubuntu"
zester 'G@os:ubuntu' state.apply ...
zester 'G@os:Ubuntu' state.apply ...
zester 'G@os:UBUNTU' state.apply ...Examples
Match by operating system
zester 'G@os:ubuntu' state.apply apt.upgradeMatch by nested fact
zester 'G@os.family:debian' state.apply apt.updateMatch by CPU count with comparison
zester 'G@cpu_count:>=4' state.apply performance.tuneMatch by memory with comparison
zester 'G@memory_gb:>16' state.apply java.heap_configExclude an OS
zester 'G@os:!=windows' state.apply linux.hardenMatch using the I@ alias
zester 'I@role:webserver' state.apply nginx.setupValidation
A fact expression must contain a : separator and a non-empty key:
# Missing separator
zester 'G@osubuntu' state.apply base.pingError: resolve target "G@osubuntu": target: create matcher:
target: fact expression must contain ':' separator: "G@osubuntu"How It Works
- The
G@orI@prefix is stripped. - The expression is split at the first
:into key and value parts. - The value part is checked for a comparison operator prefix.
- During matching, the key is resolved via dot-notation traversal of the peel's facts map.
- The resolved value is converted to a string and compared using the detected operator.
Regular Expressions
Regex targeting matches peel IDs against a regular expression. Expressions use the E@ prefix to signal regex mode.
Compound Expressions
Compound expressions combine multiple matchers using boolean operators. This is the most powerful targeting mode, allowing you to build precise target sets by intersecting, unioning, and negating different matcher types.