> ## Documentation Index
> Fetch the complete documentation index at: https://docs.runlayer.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Regular Expression Syntax

> The regex features Runlayer supports (RE2) for policy conditions and custom PII rules

Runlayer evaluates every administrator-supplied regular expression with
[Google RE2](https://github.com/google/re2/wiki/Syntax). RE2 matches in
guaranteed linear time, so a pattern can never cause a
[ReDoS](https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS)
slowdown — but in exchange it accepts a slightly narrower syntax than Python's
`re` or PCRE. This page lists what is supported so your patterns compile on the
first save.

<Note>
  This applies everywhere you can enter a regex as an administrator: policy
  condition operators (`regex`, `not_regex`, `list_regex`, `list_not_regex`,
  `list_any_not_regex` — see [Policies](/platform-policies)) and custom PII
  rules (see [Runlayer ToolGuard](/runlayer-toolguard)). A **literal** pattern
  RE2 cannot compile is rejected when you save, with the reason shown inline.
  A **dynamic** policy pattern (a reference like `$payload.pattern`, resolved
  per request) can only be checked at evaluation time: if the resolved value
  fails to compile, the condition evaluates as no-match — so a deny rule built
  on it does **not** block that request. Prefer literal patterns for
  enforcement.
</Note>

## Matching semantics

The two surfaces apply a pattern differently, and it changes how you anchor:

* **Policy conditions** match the **entire field value** (RE2 `fullmatch`,
  case-sensitive). A prefix like `^https?://` never matches a full URL on its
  own — write the whole shape, e.g. `https?://[^/]*\.example\.com(/.*)?`.
  Leading `^` and trailing `$` are redundant (but harmless) here.
* **Custom PII rules** **search within** the scanned text (case-insensitive),
  so a pattern matches anywhere unless you anchor it; `\b`, `^`, and `$`
  behave as position assertions inside the text.

## Supported

| Feature                  | Syntax                                                                                                                                                                                    | Example                                                     |
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------- |
| Literals & escapes       | `a`, `\.`, `\\`                                                                                                                                                                           | `example\.com`                                              |
| Character classes        | `[...]`, `[^...]`, ranges                                                                                                                                                                 | `[A-Za-z0-9_-]`                                             |
| Any character            | `.` (any char except newline; to include newline use `[\s\S]`, or `(?s)` in policy conditions — the PII rule editor rejects inline flags)                                                 | `a.c`                                                       |
| Quantifiers              | `*`, `+`, `?`, `{n}`, `{n,}`, `{n,m}` — counted bounds max out at **1000**; `a{1001}` is rejected with `invalid repetition size`                                                          | `\d{3,4}`                                                   |
| Lazy quantifiers         | `*?`, `+?`, `??`, `{n,m}?`                                                                                                                                                                | `<.+?>`                                                     |
| Alternation              | `a\|b`                                                                                                                                                                                    | `GET\|POST\|PUT`                                            |
| Grouping                 | `(...)`, non-capturing `(?:...)`, named `(?P<name>...)`                                                                                                                                   | `(?:v\d+)`                                                  |
| Anchors                  | `^`, `$`, `\A`, `\z`, `\b`, `\B`                                                                                                                                                          | `\btoken\b` (see [Matching semantics](#matching-semantics)) |
| ASCII shorthand classes  | `\d`, `\w`, `\s` (and `\D`, `\W`, `\S`)                                                                                                                                                   | `\w+@\w+\.\w+`                                              |
| Unicode property classes | `\p{L}`, `\p{Nd}`, `\p{Z}`, … (and `\P{...}`)                                                                                                                                             | `[\p{L}\p{N}_]+`                                            |
| Inline flags             | `(?i)` ignore-case, `(?m)` multi-line, `(?s)` dot-all — **policy conditions only**; custom PII rules already match case-insensitively, and the PII rule editor rejects inline-flag groups | `(?i)admin`                                                 |

## Not supported

RE2 rejects these; a pattern using them fails to save:

| Feature                            | Instead                                                                                                                                                                                                                                                                                                                                                  |
| ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Lookahead `(?=...)`, `(?!...)`     | Rephrase positively, or use a `not_regex` / `list_any_not_regex` complement (see [Policies](/platform-policies)).                                                                                                                                                                                                                                        |
| Lookbehind `(?<=...)`, `(?<!...)`  | Match the surrounding characters explicitly.                                                                                                                                                                                                                                                                                                             |
| Backreferences `\1`, `(?P=name)`   | Repeat the sub-pattern literally — **only equivalent when the group matches one fixed value**. `(\w+)-\1` has no RE2 equivalent: repeating gives `(\w+)-\w+`, which also matches unequal pairs like `foo-bar`. That broadening adds false positives to a PII rule and, worse, **widens an allow policy** — enforce equality outside the pattern instead. |
| `\Z` end-of-text anchor            | Use `\z` (RE2's spelling).                                                                                                                                                                                                                                                                                                                               |
| Verbose / free-spacing mode `(?x)` | Write the pattern without embedded whitespace/comments.                                                                                                                                                                                                                                                                                                  |

## Differences from Python `re`

Even when a pattern compiles, a few behaviors differ from Python's `re`.
These matter most for non-ASCII input:

<Warning>
  `\d`, `\w`, `\s`, and `\b` are **ASCII-only** in RE2, whereas Python's `re`
  is Unicode-aware by default. `\w+` matches `cafe` but not `café`, and `\d+`
  matches `123` but not the Arabic-Indic digits `١٢٣`. If a rule must catch
  Unicode letters or digits, use the property classes instead:

  * Unicode "word" character: `[\p{L}\p{N}_]` instead of `\w`
  * Unicode digit: `\p{Nd}` instead of `\d`
  * Unicode whitespace: `[\t-\r \x1c-\x1f\x85\p{Z}]` instead of `\s` —
    `\p{Z}` alone covers only separator characters, so it would stop
    matching tabs, newlines, and other control whitespace that Python's
    `\s` includes
</Warning>

* **`$` matches end-of-text only** — not the position just before a trailing
  newline (Python's default `$` does). Add `(?m)` if you want `$` to match at
  the end of each line.
* **Custom PII rules always ignore case** — case-insensitive matching is
  applied unconditionally, not opted into: a lowercase pattern like `secret`
  also masks `SECRET` and `Secret`. Case-insensitive matching uses Unicode
  simple case-folding, and Runlayer additionally folds the Python-specific
  dotted/dotless Turkish `İ`/`ı` to `i` in both the scanned text and the rule
  pattern, so obfuscation with those characters is still caught. **Policy
  conditions** are matched case-sensitively as written; a condition using an
  inline `(?i)` gets RE2's own case-folding only, with no extra preprocessing.

## Quick conversions

| If you had (Python / PCRE)  | Use (RE2)        |
| --------------------------- | ---------------- |
| `\w+` over Unicode text     | `[\p{L}\p{N}_]+` |
| `\d{3}` over Unicode digits | `\p{Nd}{3}`      |
| `foo\Z`                     | `foo\z`          |

### Converting a lookahead deny

A negative lookahead has no RE2 equivalent inside the pattern — the fix is to
move the negation into the **operator** and keep the pattern positive.
Example: a deny rule that fires when the recipient is outside the corporate
domain (remember policy conditions [match the entire field
value](#matching-semantics)):

|                           | Operator    | Pattern                          |
| ------------------------- | ----------- | -------------------------------- |
| Before (Python lookahead) | `regex`     | `^(?!.*@corp\.example\.com$).*$` |
| After (RE2)               | `not_regex` | `.*@corp\.example\.com`          |

The deny now fires when the recipient does **not** end with
`@corp.example.com`. For a list-valued field, use `list_any_not_regex` with
the same pattern — it fires when at least one entry doesn't match. See
[Policies](/platform-policies) for the full operator reference.

For the complete grammar, see the
[official RE2 syntax reference](https://github.com/google/re2/wiki/Syntax).
