v0.23.0 is a small, security-flavored release. Two new defaults: fields give you first-class control over what tool nodes are allowed to shell out — something you previously could only reach by dropping to raw DOT or patching the Workflow.Vars map in Tracker directly.
Dippin tool nodes execute real shell commands. Tracker’s runtime reads an allowlist and denylist at start time to decide which commands are permitted. Until this release, those lists lived in graph.Attrs — DOT-level attributes. If you wanted to set them from a .dip file, your options were:
Workflow.Vars programmatically via Tracker’s library API.Both defeat the purpose of .dip as the authoring format. The feature requested by #28 was simple: accept these two keys in the defaults: block, same as every other workflow-level setting.
workflow ToolSafety
goal: "Constrained shell execution"
start: Start
exit: Done
defaults
model: claude-sonnet-4-6
# Glob allowlist — commands matching ANY pattern are permitted
tool_commands_allow: "git *,make *,npm test,npm run *"
# Patterns appended to tracker's built-in default denylist
tool_denylist_add: "rm -rf /,dd if=*"
tool RunTests
timeout: 60s
command:
set -eu
npm test
# ...
Both values are comma-separated glob patterns. Dippin-lang itself doesn’t validate the glob syntax and stores the string verbatim — tracker owns splitting and matching semantics at runtime, so there’s no parse-time normalization that could drift from tracker’s parser.
Both fields round-trip through parse → format → DOT export → migrate, so DOT-authored workflows and migrations from legacy DOT files carry them through unchanged. See examples/tool_safety.dip for a working example.
Landing this feature required tightening how ExportDOT emits graph-level attributes. Previously the header wrote each attribute as a bare statement:
digraph ToolSafety {
rankdir=TB;
node [fontname="Helvetica"];
tool_commands_allow="git *,make *";
...
}
Valid DOT, but the migrate parser only accepts attributes inside a graph [...] block. Round-tripping a .dip through export → Migrate → reparse would silently drop the attributes. The header now emits a single block:
digraph ToolSafety {
node [fontname="Helvetica"];
edge [fontname="Helvetica"];
graph [rankdir=TB, tool_commands_allow="git *,make *"];
...
}
Still valid DOT. Graphviz consumers are unaffected. If you have tooling that grep’s the old bare-statement form — that’s where the breakage lives; switch to parsing the graph [...] block.
If you previously smuggled tool_commands_allow or tool_denylist_add through vars: (because defaults: rejected them), move them to defaults: now. These keys are reserved in the exporter starting with this release, so values in vars: are silently dropped from DOT output. The workaround was never documented — but if you found it, the fix is a one-line move.
Full notes in CHANGELOG.md. The tracker side of this is tracker#169 (allowlist runtime, shipped) and tracker#168 (denylist runtime, pending). Once tracker#168 lands, tool_denylist_add gets teeth — until then, the field parses and round-trips but tracker only enforces its built-in defaults.