Regex reference

Guardrail patterns are compiled with RE2, the regular-expression engine used by Go. If you have written a regex before, almost everything works exactly as you expect. Three things do not, and the reason is worth understanding.

What is not supported

Not supportedExample
Lookaheadfoo(?=bar), foo(?!bar)
Lookbehind(?<=foo)bar, (?<!foo)bar
Backreferences(\w+)\s+\1

These are not missing features. RE2 excludes them deliberately, because they are what make a regex engine capable of catastrophic backtracking — the failure mode where a short, innocuous-looking pattern takes exponential time on a crafted input.

Every guardrail pattern runs on every request your organisation makes, against text an attacker can choose. A backtracking engine in that position is a denial-of-service vector aimed at your entire AI capability. RE2 matches in time linear in the length of the input, always, regardless of the pattern. You cannot write a rule that hangs the firewall, and nobody can send a prompt that does.

That trade is the right one, and it is not negotiable.

What is supported

Everything else you are likely to reach for:

FeatureExample
Character classes[a-z], [^0-9], \d, \w, \s
Quantifiers*, +, ?, {3}, {2,5}, {13,16}
Lazy quantifiers*?, +?, {2,5}?
Alternation`foo
Grouping(abc)+, non-capturing (?:abc)+
Named groups(?P<name>\w+)
Anchors^, $, \b, \B
Case-insensitive flag(?i)secret
Multiline / dot-all flags(?m), (?s)
Unicode classes\p{L}, \p{Han}

Porting from PCRE

If you are bringing patterns over from a secret scanner, a SIEM rule, or grep -P, the lookarounds are what will break. Common rewrites:

A negative lookahead used to exclude a known-safe prefix:

(?<!example\.)api\.internal      ✗ not supported

Instead, match what you do want and accept the broader match, then narrow it with a more specific pattern — or split into two rules where one flags and a higher-priority one allows by simply not matching. There is no “exclude” primitive; see tuning false positives.

A backreference used to find repeated tokens:

(\b\w+\b)\s+\1                   ✗ not supported

RE2 cannot express this at all. Rewrite around the specific literal you care about.

The failure mode you must know about

Warning

A rule whose pattern does not compile is silently skipped.

It stays in your policy. It shows as enabled. It matches nothing, forever. The gateway does not error, does not warn the caller, and does not fail the request — it simply evaluates the remaining rules as though that one did not exist.

This means a typo in a regex can silently disable a control you believe is protecting you.

This is why the CLI compiles every pattern locally and tells you:

vulnetix ai-firewall status

A bad pattern shows up as a guardrail_pattern_invalid warning, with a hint if the cause is a lookaround. Run it after every rule change. In CI:

vulnetix ai-firewall status --strict

The policy-as-code path validates patterns too: apply rejects the whole file if any pattern will not compile, so a bad regex never reaches your organisation’s policy in the first place. That is the safest way to manage rules.

The same applies to max_messages

A max_messages rule whose pattern is not an integer is skipped by exactly the same mechanism. --pattern 40 is a rule; --pattern forty is a rule that does nothing.

Testing a pattern

RE2’s syntax is documented in the Go regexp/syntax package. Any Go regex playground will tell you whether a pattern compiles and what it matches — that is exactly the engine the firewall runs.

Then deploy it at --action flag and watch what it does to real traffic before you let it block or rewrite anything. See rollout.