The contents, linecount, hash, and digest template filters read files
through a shared byte budget so a checkout entry cannot exhaust Netsuke's
memory or CPU. The default budget is 8 MiB per read, matching the fetch()
response limit.
The reading filters also refuse to follow a symlink as the final path component and reject anything that is not a regular file once opened, including FIFOs and device nodes. A symlinked directory used inside a path is unaffected; only the final entry is checked. On Windows the same refusal covers every reparse point, not only symlinks: junctions, volume mount points, and other tags such as deduplication or cloud placeholders are all rejected, including tags Windows may add later.
follow_symlinks=true waives that final-component refusal, and only that. The
capability that anchors every read in the workspace is unaffected, so a link
whose target is written as an absolute path is still refused with a diagnostic
reporting that a path led outside the filesystem — even when the target is in
fact inside the workspace. Relativity of the link target, not containment of
the resolved path, is what the resolver tests. A template that reads through a
relative-target link is the supported case; on Windows a junction cannot be
one, because mklink records an absolute target and so is always refused under
either policy. Templates that deliberately read through a final symlink can
pass the opt-in to accept it:
{{ 'generated/version.txt' | contents(follow_symlinks=true) }}
A single call may lower the budget with max_bytes, but never raise it above
the configured ceiling:
{{ 'fixtures/big.bin' | hash(max_bytes=1024) }}
Reads that exceed the budget fail with a diagnostic naming the path and the
limit, never the file contents. Raise the operator budget when legitimate
builds hash large artefacts; prefer per-call max_bytes narrowing when a
manifest merely wants to bound one input.