vane 0.9.2

A flow-based reverse proxy with multi-layer routing and programmable pipelines.
---
title: Template System
description: The variable substitution and dynamic configuration engine in Vane.
icon: Braces
---

import { Steps, Step } from 'fumadocs-ui/components/steps';

The Template System is Vane's dynamic engine, allowing plugin configurations to reference real-time metadata from the [KV Store](./kv-store) or specific protocol contexts (like HTTP headers or TLS SNI).

Vane uses a double-brace syntax: `{{variable_name}}`.

## Core Mechanics

The system operates as a recursive-descent parser and resolver, enabling powerful features like nested variable resolution.

### Nested Variables

Templates are resolved from the inside out. Consider the example: `{{kv.{{conn.protocol}}_backend}}`

<Mermaid
  chart='
graph LR;
    subgraph Step1 [Phase 1: Parse Inner]
        Inner["{{conn.protocol}}"] -- Lookup --> Value1["http"]
    end

    subgraph Step2 [Phase 2: Construct Key]
        Concat["kv. + http + _backend"] --> Key["kv.http_backend"]
    end

    subgraph Step3 [Phase 3: Final Resolution]
        Value2["backend-01"]
    end

    Value1 --> Concat
    Key --> Value2

'
/>

1.  The inner variable `{{conn.protocol}}` is resolved first (e.g., to `http`).
2.  The resulting string `kv.http_backend` becomes the key for the outer resolution.
3.  The final value is looked up in the KV store using the constructed key.

### JSON Integration

The engine is not limited to plain strings. Through `resolve_inputs()`, Vane can recursively traverse complex JSON structures (Arrays and Objects), identifying and resolving every string-based template found within.

## The Resolution Pipeline

<Steps>
<Step>

### Parsing (AST Generation)

The `parser.rs` module converts the raw string into an Abstract Syntax Tree (AST) composed of `Text` and `Variable` nodes.

<Mermaid
	chart="
graph LR
    Raw[Raw String] --> Parser[AST Parser]
    Parser --> Nodes[AST: Text + Variable Nodes]
"
/>

</Step>
<Step>

### Recursive Resolution

The `resolver.rs` module traverses the AST. If a `Variable` node contains nested parts, it recursively calls the resolver to determine the final key name.

<Mermaid
	chart="
graph LR
    AST[AST Nodes] --> Resolve[Recursive Resolver]
    Resolve --> Nested{Nested?}
    Nested -- Yes --> Resolve
    Nested -- No --> Key[Final Key Name]
"
/>

</Step>
<Step>

### Context Lookup

The resolved key is passed to a `TemplateContext`. While usually linked to the connection's `KvStore`, the context can also "hijack" data directly from protocol streams.

<Mermaid
	chart="
graph LR
    Key[Key Name] --> Context{TemplateContext}
    Context --> KV[KV Store Lookup]
    Context --> Hijack[Protocol Hijacking]
    KV & Hijack --> Value[Final String Value]
"
/>

</Step>
</Steps>

## Security: Injection Protection

Vane implements a critical security layer to prevent "Template Injection" attacks.

If a dynamically resolved key name (the string inside the braces) contains the characters `{` or `}`, the resolver **immediately refuses to perform the lookup**.

**Why this matters:**
Imagine an attacker provides a value that is later used as part of a template key. If they provide `{{system.admin_token}}`, and Vane naively resolves it, they could trick the engine into leaking sensitive internal variables. By banning template syntax within resolved keys, Vane ensures that data and logic remain strictly separated.

## Resource Limits

To protect the engine's performance, the following limits are enforced via environment variables:

| Variable                   | Default | Description                                            |
| :------------------------- | :------ | :----------------------------------------------------- |
| `MAX_TEMPLATE_DEPTH`       | `5`     | Maximum recursion depth for nested variables and JSON. |
| `MAX_TEMPLATE_RESULT_SIZE` | `65536` | Maximum size (64KB) for a fully resolved string.       |
| `MAX_TEMPLATE_PARSE_NODES` | `50`    | Maximum nodes allowed in a single template AST.        |

<Callout type="info" title="Lazy Evaluation">
Template resolution is lazy. For example, referencing `{{req.body}}` in Layer 7 will trigger the on-demand buffering of the HTTP request body only at the moment the template is resolved.
</Callout>