templating

package
v1.2.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 11, 2026 License: MIT Imports: 18 Imported by: 0

README

Sarracenia Templating Engine

Go Reference Go Version Part of Sarracenia AGPLv3 License

A high-performance, extensible Go templating engine designed for generating complex, dynamic, and obfuscated web content.

Built for the Sarracenia tarpit, this engine specializes in creating plausible-looking but randomly generated HTML structures, integrated directly with Markov text generation sources.

Installation

go get github.com/CTAG07/Sarracenia/pkg/templating

Quick Start

package main

import (
	"bytes"
	"log/slog"
	"os"

	"github.com/CTAG07/Sarracenia/pkg/templating"
)

func main() {
	logger := slog.New(slog.NewTextHandler(os.Stdout, nil))
	
    // Initialize Config
	cfg := templating.DefaultConfig()
	
    // Create Manager (Pass nil for generator if Markov features aren't needed)
	tm, _ := templating.NewTemplateManager(logger, nil, cfg, "./data")

	// Render a template by name
	var buf bytes.Buffer
	if err := tm.Execute(&buf, "page.tmpl.html", nil); err != nil {
		panic(err)
	}
    
    // Output: <html>...random content...</html>
}

Configuration

The TemplateConfig struct controls the behavior and safety limits of the templating engine.

Key Description Default
markov_enabled Controls whether markov functions use the generator. Falls back to random if false. true
markov_separator Separator used by the markov tokenizer. ""
markov_eoc End-of-chain marker used by the markov tokenizer. ""
markov_split_regex Regex for splitting tokens in the markov tokenizer. ""
markov_eoc_regex Regex for detecting EOC tokens. ""
markov_separator_exc_regex Regex for tokens that should not have a separator prefix. ""
markov_eoc_exc_regex Regex for tokens that should not have an EOC suffix. ""
path_whitelist URL paths considered safe; excluded from random link generation. []
min_subpaths Minimum number of segments in generated URL paths. 1
max_subpaths Maximum number of segments in generated URL paths. 5
max_json_depth Hard limit on recursion depth for randomJSON. 8
max_nest_divs Hard limit on recursion depth for nestDivs. 50
max_table_rows Maximum rows for randomComplexTable. 100
max_table_cols Maximum columns for randomComplexTable. 50
max_form_fields Maximum fields for randomForm. 75
max_style_rules Maximum complex CSS rules for randomStyleBlock. 200
max_css_vars Maximum interdependent CSS variables for randomCSSVars. 100
max_svg_elements Complexity limit for randomSVG. 7
max_js_content_size Maximum content size (bytes) for jsInteractiveContent. 1048576 (1MB)
max_js_waste_cycles Maximum waste loop iterations for jsInteractiveContent. 1,000,000

Template Function Reference

The engine exposes a wide range of custom functions to templates.

Content (funcs_content.go)
Signature Description
markovSentence modelName maxLength Generates a thematic sentence from the specified Markov model.
markovParagraphs modelName count minSentences maxSentences minLength maxLength Generates paragraphs of thematic text from the specified Markov model.
randomWord Returns a single random word from the loaded dictionary.
randomSentence length Generates a nonsensical sentence of a given length.
randomParagraphs count minSentences maxSentences minLength maxLength Generates a nonsensical set of paragraphs with lengths in the range of minLength to maxLength
randomString type length Generates a random string. Types: username, email, uuid, hex, alphanum.
randomDate layout start end Generates a random, formatted date within a range from three strings.
randomJSON depth elements len Generates a random, nested JSON object string. Capped by MaxJSONDepth.
Structure (funcs_structure.go)
Signature Description
randomForm count styleCount Generates a <form> with count varied input fields. Capped by MaxFormFields.
randomDefinitionData count sentenceLength Returns a slice of {Term, Def} structs for building <dl> lists.
nestDivs depth Generates depth deeply nested <div> elements. Capped by MaxNestDivs.
randomComplexTable rows cols Generates an irregular <table> with random colspan. Capped by MaxTableRows/Cols.
Styling (funcs_styling.go)
Signature Description
randomColor Returns a random hex color code string (e.g., #a1f6b3).
randomId prefix length Generates a random HTML ID string with a prefix.
randomClasses count Returns a space-separated string of count random, utility-style class names.
randomCSSStyle count Returns a string of count random CSS property declarations.
randomInlineStyle count Returns a complete style="..." attribute with count random properties.
Signature Description
randomLink Generates a plausible, root-relative URL path, avoiding the PathWhitelist.
randomQueryLink keyCount Generates a random path and appends keyCount random query parameters.
Logic & Control (funcs_logic.go)
Signature Description
repeat count Returns a slice for use with range to loop count times.
list item1 item2 ... Returns a slice from the provided arguments.
randomChoice slice Returns a random item from a slice.
randomInt min max Returns a random integer in [min, max).
Simple Math & Logic (funcs_simple.go)
Signature Description
add a b Returns a+b
sub a b Returns a-b
div a b Returns a/b (Or 0 if b=0)
mult a b Returns a*b
mod a b Returns a%b (Or 0 if b=0)
max a b Returns the maximum of a and b
min a b Returns the minimum of a and b
inc i Increments i by one
dec i Decrements i by one
and arg1 arg2 ... Returns true if all of the bool args are true
or arg1 arg2 ... Returns true if any of the bool args are true
not arg Returns !arg
isSet value Returns true if value is not its "zero" value (not nil, "", 0, etc.).
Computationally Expensive (funcs_expensive.go)
Signature Description
randomStyleBlock type count Generates a <style> block with count complex/nested CSS rules. Capped by MaxStyleRules.
randomCSSVars count Generates a <style> block with a chain of interdependent CSS custom properties. Capped by MaxCssVars.
randomSVG type complexity Generates a complex inline SVG. complexity controls detail. Capped by MaxSvgElements. Types: "fractal", "filters"
jsInteractiveContent tag content cycles Generates a JS element that decodes content after running a CPU waste loop for cycles iterations. Capped by MaxJsContentSize/WasteCycles.

Advanced Usage: The Composition Pattern

The engine uses a strict file-naming convention to distinguish between stand-alone pages and reusable components.

  • *.tmpl.html: Main Templates. These are complete pages (e.g., page.tmpl.html).
  • *.part.html: Partials. These are reusable components (e.g., layout.part.html, _header.part.html). They should not be rendered directly and should only be used within a complete template.
Example Architecture

1. Base Layout (data/templates/layout.part.html)

{{define "layout.part.html"}}
<!DOCTYPE html>
<html>
<body>
    {{template "content" .}}
</body>
</html>
{{end}}

2. Main Page (data/templates/page.tmpl.html)

{{/* Extend the layout */}}
{{template "layout.part.html" .}}

{{/* Define the content block */}}
{{define "content"}}
    <h1>{{markovSentence "tech-model" 10}}</h1>
{{end}}

Benchmarks

The results below were captured on the following system and provide a performance profile for various content generation categories.

  • CPU: 13th Gen Intel(R) Core(TM) i9-13905H
  • OS: Windows 11
  • Go: 1.24.5

The templates used for the benchmarks are as follows:

{{/* BenchmarkExecute_Simple */}}
<h1>{{randomWord}}</h1><p>{{randomSentence 5}}</p>

{{/* BenchmarkExecute_Styling */}}
<div id="{{randomId " pfx" 8}}" class="{{randomClasses 5}}" style="{{randomInlineStyle 5}}"></div>

{{/* BenchmarkExecute_CPUIntensive */}}
{{randomSVG "fractal" 10}} {{jsInteractiveContent "div" "secret" 1000}}

{{/* BenchmarkExecute_Structure */}}
{{nestDivs 15}} {{randomComplexTable 10 10}}

{{/* BenchmarkExecute_DataGeneration */}}
{{randomForm 10 5}}
<script type="application/json">{{randomJSON 4 5 10}}</script>

{{/* BenchmarkExecute_Markov */}}
<h1>{{markovSentence "test_model" 15}}</h1><p>{{markovParagraphs "test_model" 2 3 5 10 20}}</p>
Template Execution Performance
Benchmark Time/Op Mem/Op Allocs/Op
Execute/Simple 1.70 µs 723 B 27
Execute/Styling 4.49 µs 1.9 KB 75
Execute/CPUIntensive 6.09 µs 3.0 KB 80
Execute/Structure 21.37 µs 17.2 KB 460
Execute/DataGeneration 77.57 µs 51.9 KB 792
Execute/Markov 278.84 µs 70.8 KB 2,373
Running Benchmarks
cd pkg/templating
go test -bench . -benchmem

Documentation

Overview

Package templating provides a high-performance, filesystem-based Go template engine. It is designed for generating dynamic, complex, and plausible-looking web content with minimal server overhead.

It includes a rich library of custom template functions for generating everything from structured data (JSON, forms) and styled elements (CSS, SVGs) to thematic text via an integrated, SQLite-backed Markov generator. The engine is fully configurable with safety limits to prevent abuse and supports hot-reloading of templates from the filesystem, enabling easy updates post-deployment.

For a complete list of template functions and usage examples, see the README.md file.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func InitWordList

func InitWordList(path string) error

InitWordList loads the global dictionary of words from a file at the given path. It is designed to be called once at application startup. It uses a sync.Once to ensure the word list is loaded only a single time, making subsequent calls a no-op. An error is returned if the file cannot be read.

Types

type DefinitionData

type DefinitionData struct {
	Term string
	Def  string
}

DefinitionData is a simple struct to hold a term and its definition. It is used by the randomDefinitionData template function to return a slice that templates can easily range over to build definition lists (<dl>).

type StyleBlock

type StyleBlock struct {
	// Style contains the full <style>...</style> block as safe HTML.
	Style template.HTML
	// Class contains the unique CSS class name (e.g., "c-a1b2c3d4") to be used
	// in an element's class attribute.
	Class template.CSS
}

StyleBlock is a struct returned by the randomStyleBlock template function. It allows the template to access both the generated <style> block HTML and the unique parent class name required to apply those styles to a specific element.

type TemplateConfig

type TemplateConfig struct {
	// MarkovEnabled controls whether the `markov` template functions will use the
	// markov generator for content generation. If false, they fall back to using
	// the `random` functions.
	MarkovEnabled bool `json:"markov_enabled"`

	// MarkovSeparator sets the separator to be used by the markov tokenizer. (optional)
	MarkovSeparator string `json:"markov_separator"`

	// MarkovEoc sets the eoc to be used by the markov tokenizer. (optional)
	MarkovEoc string `json:"markov_eoc"`

	// MarkovSplitRegex sets the regex to be used by the markov tokenizer for splitting tokens. (optional)
	MarkovSplitRegex string `json:"markov_split_regex"`

	// MarkovEocRegex sets the regex to be used by the markov tokenizer for detecting EOC tokens. (optional)
	MarkovEocRegex string `json:"markov_eoc_regex"`

	// MarkovSeparatorExcRegex sets the regex to be used by the markov tokenizer for determining which tokens should
	// not have a separator put in front of them. (optional)
	MarkovSeparatorExcRegex string `json:"markov_separator_exc_regex"`

	// MarkovEocExcRegex sets the regex to be used by the markov tokenizer for determining which tokens should not
	// have an EOC token put after them. (optional)
	MarkovEocExcRegex string `json:"markov_eoc_exc_regex"`

	// PathWhitelist is a list of URL paths that are considered safe and should not
	// be used for randomly generated links (e.g., "/api", "/admin"). This prevents
	// collisions with real application endpoints.
	PathWhitelist []string `json:"path_whitelist"`

	// MinSubpaths defines the minimum number of segments in a generated URL path.
	MinSubpaths int `json:"min_subpaths"`

	// MaxSubpaths defines the maximum number of segments in a generated URL path.
	MaxSubpaths int `json:"max_subpaths"`

	// MaxJSONDepth sets a hard upper limit on the recursion depth for the randomJSON function.
	MaxJSONDepth int `json:"max_json_depth"`

	// MaxNestDivs sets a hard upper limit on the recursion depth for the nestDivs function.
	// This prevents templates from requesting a depth that could crash a browser.
	MaxNestDivs int `json:"max_nest_divs"`

	// MaxTableRows sets the maximum number of rows for the randomComplexTable function.
	MaxTableRows int `json:"max_table_rows"`

	// MaxTableCols sets the maximum number of columns for the randomComplexTable function.
	MaxTableCols int `json:"max_table_cols"`

	// MaxFormFields sets the maximum number of fields for the randomForm function.
	MaxFormFields int `json:"max_form_fields"`

	// MaxStyleRules sets the maximum number of complex CSS rules generated by the
	// randomStyleBlock function.
	MaxStyleRules int `json:"max_style_rules"`

	// MaxCssVars sets the maximum number of interdependent CSS custom properties
	// generated by the randomCSSVars function.
	MaxCssVars int `json:"max_css_vars"`

	// MaxSvgElements sets a general limit for the complexity of generated SVGs,
	// such as the recursion depth for fractals or the number of filter primitives.
	MaxSvgElements int `json:"max_svg_elements"`

	// MaxJsContentSize sets the maximum size in bytes of the content to be encoded
	// and rendered by the jsInteractiveContent function.
	MaxJsContentSize int `json:"max_js_content_size"`

	// MaxJsWasteCycles sets the maximum number of iterations for the CPU waste
	// loop within the jsInteractiveContent function.
	MaxJsWasteCycles int `json:"max_js_waste_cycles"`
}

TemplateConfig holds all configuration options for the templating engine. It provides granular control over the generated content, including safety limits for computationally expensive functions to prevent abuse and ensure stability for both the server and the client's browser.

func DefaultConfig

func DefaultConfig() *TemplateConfig

DefaultConfig returns a new TemplateConfig populated with safe, sensible default values. The PathWhitelist is empty by default, assuming that any non-API path will serve tarpit content.

type TemplateManager

type TemplateManager struct {
	// contains filtered or unexported fields
}

TemplateManager is the central controller for the templating engine. It manages the template set, configuration, function map, and connections to other services like the Markov generator. It is responsible for loading, parsing, and executing templates in a concurrent-safe manner. All methods are concurrent-safe.

func NewTemplateManager

func NewTemplateManager(logger *slog.Logger, markovGen *markov.Generator, config *TemplateConfig, dataDir string) (*TemplateManager, error)

NewTemplateManager creates, initializes, and returns a new TemplateManager. It requires a logger, an optional Markov generator (can be nil if config.MarkovEnabled is false), a configuration, and the path to the data directory which must contain a "templates" subdirectory and a "wordlist.txt" file. It performs an initial Refresh to load all templates and models.

func (*TemplateManager) Execute

func (tm *TemplateManager) Execute(w io.Writer, name string, data interface{}) error

Execute renders a specific template by name, writing the output to the provided io.Writer. The `data` argument is passed to the template and can be used to provide context or dynamic values.

func (*TemplateManager) ExecuteTemplateString added in v1.0.0

func (tm *TemplateManager) ExecuteTemplateString(w io.Writer, content string, data interface{}) error

ExecuteTemplateString parses and executes a raw template string using the manager's function map. This is ideal for testing or previewing templates without saving them to disk.

func (*TemplateManager) GetConfig added in v1.0.0

func (tm *TemplateManager) GetConfig() TemplateConfig

GetConfig returns a copy of the current configuration. This mainly exists for concurrency-safety reasons.

func (*TemplateManager) GetRandomTemplate

func (tm *TemplateManager) GetRandomTemplate() string

GetRandomTemplate returns the name of a randomly selected template from the set of loaded full templates. This is the primary mechanism for serving varied and unpredictable pages to web scrapers.

func (*TemplateManager) GetTemplateDir added in v1.0.0

func (tm *TemplateManager) GetTemplateDir() string

GetTemplateDir returns the template dir that the TemplateManager uses. This mainly exists for concurrency-safety reasons as well.

func (*TemplateManager) GetTemplateNames added in v1.0.0

func (tm *TemplateManager) GetTemplateNames() []string

GetTemplateNames returns a slice of the loaded template names. This mainly exists for concurrency-safety reasons, and because it returns the names of partial templates as well.

func (*TemplateManager) Refresh

func (tm *TemplateManager) Refresh() error

Refresh reloads all templates from the filesystem and, if enabled, refreshes the list of available Markov models from the database. This function allows for updates to templates and models without restarting the application.

func (*TemplateManager) SetConfig

func (tm *TemplateManager) SetConfig(config *TemplateConfig)

SetConfig applies a new configuration to the TemplateManager. This allows for changes to the engine's behavior, such as updating safety limits or the path whitelist, without needing to restart the application.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL