Validation rules¶
Every value that flows from you — a CLI flag, a wizard answer, a manifest field — into a template is validated before it is rendered. The rules are tight on purpose: constraining the character class removes most of the template-injection class outright, and the escape filters are only defence in depth behind it.
Every rule normalises its input to Unicode NFC before checking, so homoglyph and combining-mark tricks fail rather than sneaking past a regex. Rejections name the field and the rule; they do not echo the value above debug level.
The rules at a glance¶
| Rule | Applies to | Pattern or limit |
|---|---|---|
| Tool / command name | generate project --name, top-level command names |
^[a-z][a-z0-9-]{0,63}$, not reserved; top-level command names must also not be a Rust keyword |
| Description | --description, --about |
≤ 500 bytes after NFC; no control characters except tab; no {{ or }} |
| Environment prefix | env-prefix fields | empty, or ^[A-Z][A-Z0-9_]{0,31}$ |
| Command path | generate command, remove command |
each /-separated segment matches ^[a-z][a-z0-9-]{0,63}$; at most 4 segments |
| Flag name | generate flag, remove flag, and setting names |
^[a-z][a-z0-9-]{0,63}$, ≤ 64 bytes, must not start with -- |
Names¶
Lowercase ASCII, a letter first, then letters, digits or hyphens, up to 64 characters. No underscores, no uppercase, no dots, no path separators.
$ rtb generate project --name version --non-interactive
x invalid tool name: name: `version` is reserved (framework default or scaffolder verb)
That class is chosen so that shell metacharacters, path separators, YAML/TOML/Markdown metacharacters and Unicode look-alikes are all impossible by construction rather than by a blocklist.
Reserved command and tool names¶
A name may not shadow a framework command or a scaffolder verb:
version, doctor, update, docs, mcp, config, credentials,
telemetry, init, generate, remove, regenerate, help, and the
empty string.
The check applies to a tool name and to the first segment of a
command path only. kube/version is fine; a bare version is not — a
nested segment becomes a path string, not a top-level command.
The framework's own crate names are not on the list.
rtb generate project --name rtb succeeds and produces a tool called
rtb; nothing stops you shadowing the scaffolder's own binary name.
Rust keywords¶
A top-level command name also becomes a module name and a struct
identifier in the generated code, so a Rust keyword would not compile.
All strict, reserved-for-future-use and 2018-edition keywords are
refused, including async, await, dyn, try, box and macro.
Nested segments are exempt for the same reason as above.
Both lists are public constants (RESERVED_NAMES, RUST_KEYWORDS), so
tooling can assert against the canonical set instead of restating it.
Descriptions¶
At most 500 bytes measured after NFC normalisation, so a
normalisation that lengthens the string can push a borderline value over.
Tab is the only control character allowed. {{ and }} are refused
outright — not because the current engine would reparse them, but so that
a future move to an engine that does cannot turn stored data into a
directive.
Command paths¶
Up to four /-separated segments, each matching the name pattern, with
no leading or trailing /. Depth is capped to keep generated help trees
navigable.
The validator accepts multi-segment paths, but generate command and
remove command both reject anything containing / with "nested command
paths are not yet supported" — so in practice, one segment.
Flag names — and setting names¶
Pass the bare name. A leading -- is rejected with a message saying so,
rather than being stripped, so a script that got the calling convention
wrong fails loudly instead of quietly producing ----flag.
The same validator is used for generate setting <FIELD_NAME>, which is
why a setting cannot be snake_case even though the help text describes
it that way — see what RTB does not
do.
Where the rules live¶
crates/rtb-cli-bin/src/validate.rs, mirroring
internal/generator/validate.go in Go Tool Base field for field. The
parity is deliberate: the same concept — a tool name, an env prefix, a
command path — gets the same constraint in both scaffolders, so a team
using both does not have to hold two sets of rules in their head.
When you add a user-facing field, add its rule there and record it in
docs/development/template-security.md.