Skip to content

files Provider

runtime files <operation> <path> [content...]

Auth provider: none. The files provider never touches a network or a credential, so the Auth Engine is skipped entirely.

It still passes through the same lifecycle as every other operation: Bootstrap → Context → Policy → Execution → Audit. It exists so the runtime has exactly one dispatch path — there is no "local" shortcut around governance.

Operations

Operation Transport Notes
read <path> file
write <path> <content...> file Overwrites
append <path> <content...> file
list <path> file Directory listing
delete <path> file Denied by the compiled default policy
runtime files write ./notes.txt hello world
runtime files read ./notes.txt
runtime files append ./notes.txt another line
runtime files list .
runtime --output json files read ./notes.txt

Selective and bulk editing

Runtime 0.9.4 adds one direct, governed edit surface for local engineering files. Omit --dry-run to edit after whole-batch preflight; add it to calculate the same selection, transformations, validations, counts and diffs without writing. There is no manifest, saved plan or digest to copy.

runtime files text replace --from master --to main \
  --dir ./repos --filename build.yaml \
  --expected-files 100 --expected-per-file 1 --dry-run

runtime files yaml set --path 'containers[name=api].image' \
  --value 'app:2' --dir ./repos --filename deployment.yaml

runtime --output json files terraform add-list-item \
  --block module --label repository --attribute teams \
  --value '"platform"' --dir ./repos --include '*.tf'

Every edit takes exactly one --file or --dir. A directory walk is recursive, lexically ordered and bounded. Narrow it with --filename, repeatable --include, and repeatable --exclude. Use --expected-files, --expected-matches, and --expected-per-file when an estate change must match the reviewed shape exactly.

Format Released operations
Text exact replace; optional whole-word or whole-line matching
YAML exact replace, scalar path set, insert-after, comment-out, uncomment; numeric and keyed sequence paths
JSON typed RFC 6901 add, set/replace, remove, test
Terraform/HCL selected block attribute set, single-line list add/remove, required-provider/version update
CODEOWNERS exact-pattern owner add/remove, structural validation
Dockerfile selected-stage base image/tag update, exact replace
Markdown exact replace outside fences, heading-section replace

Literal and span-based operations preserve unmatched bytes. YAML scalar edits preserve comments, key order, quoting and unrelated whitespace. Terraform editing currently supports selected attributes and single-line lists; it is not an arbitrary HCL refactoring engine. Dockerfile editing preserves unrelated lines and validates the resulting FROM surface. Ambiguous selectors, invalid documents, multi-document YAML, unexpected counts, zero matches and malformed after-images refuse before the first write.

Default text output is the human review surface: one file table, totals, and bounded diffs. --output json returns typed file states, match counts, validation, diffs and before/after digests. Raw output is refused for a batch because it would erase file boundaries.

Commit and recovery boundary

File Engine resolves read and write descriptors for the whole selection, reads and validates every pre-image and after-image, and only then begins replacement. Immediately before each atomic same-directory replacement, it rechecks file identity and the content digest. A concurrent change therefore refuses rather than being overwritten.

Atomic replacement protects each file, not a hundred files across different filesystems. If a later commit fails, Runtime attempts bounded rollback and records each target as rolled_back, not_committed, or unknown; it never reports a false all-or-none outcome. It does not run Git, formatters, linters or external validators.

delete is denied by default

providers:
  files:
    enabled: true
    denied:
      - delete
runtime files delete ./notes.txt      # refused by policy

Remove the rule from policy-config.yaml to allow it. For files under version control, runtime command run git rm is often the better answer — it is recoverable from history, which a filesystem delete is not.

File access is granted, not assumed

Changed in 0.6.0, and it is a breaking change. Earlier releases let the File Engine read anything on the machine and write almost anywhere. It now reaches only directories your policy document grants, and a policy with no file_policy block grants nothing.

The shipped default grants the directory you ran runtime from:

file_policy:
  read_roots:
    - "."          # the working directory, resolved per request
  write_roots:
    - "."

"." follows you

Resolved per request means the grant moves with the working directory of each command — the same files read succeeds in one directory and is refused in another. Fine for a first run, confusing once capabilities run from CI or an agent. Use absolute paths in a policy you keep, and see choosing roots for how wide to make them.

Read and write are separate grants. A root in read_roots permits read and list; a root in write_roots permits write, append and delete. Neither implies the other, so a capability allowed to summarise a tree cannot also replace it.

A path has to resolve inside a granted root — it is not matched against a pattern. .., an absolute path elsewhere and a symlink pointing out of the root all reach nothing:

$ runtime files read /etc/hosts
files read /etc/hosts is outside every directory policy grants read authority to
(/home/you/project). The path is not evaluated against a pattern — it has to
resolve inside a granted root, so `..` and an absolute path elsewhere reach
nothing

If you are upgrading: a policy document you wrote yourself has no file_policy block, so every files operation will be refused until you add one. Copy the block above and narrow the roots to the trees this machine should touch — an absolute path is usually the better answer for a fleet.

Some files are refused inside any grant

These are never reachable through the File Engine, for reads as well as writes, even when they sit inside a granted root:

  • Runtime's own config.yaml, policy-config.yaml, version, specs/, commands/, logs/, audit/, enterprise/ and trust/
  • whichever governance document $RUNTIME_CONFIG_FILE or $RUNTIME_POLICY_FILE selected, wherever it lives
  • the credential stores your tools authenticate with: ~/.ssh, ~/.kube, ~/.aws, ~/.azure, ~/.config/gcloud, ~/.config/gh, ~/.docker, ~/.netrc, ~/.git-credentials, ~/.gnupg, and the paths $KUBECONFIG, $CLOUDSDK_CONFIG and $GOOGLE_APPLICATION_CREDENTIALS point at

Two things follow from how this is enforced. The refusal follows the file, not the name — a symlink to it, a hard link to it, or moving it somewhere else all still refuse. And reads are covered, which they were not before: runtime files read ~/.ssh/id_rsa used to return a private key to whatever asked, which for an AI-driven workflow means into a context window.

files write is an allowed operation, so without the governance half a single command could rewrite policy and the next command would run under the new rules.

Still writable, deliberately: the capabilities directory, cache/, and anything else inside a granted root. Authoring a capability with files write is a normal workflow.

Results are bounded

Budget Default Narrow it with
Bytes returned by read 1 MiB file_policy.max_read_bytes
Entries returned by list 1000 file_policy.max_list_entries
Directories below a root 32 file_policy.max_depth
Files selected for one edit 1000 file_policy.max_edit_files
Pre/post bytes in one edit 32 MiB file_policy.max_edit_batch_bytes
Matches in one edit 10000 file_policy.max_edit_matches
Returned diffs 1 MiB file_policy.max_edit_diff_bytes
Edit duration 30 seconds file_policy.max_edit_seconds

A document may only narrow these. A larger value is ignored, because a limit a document can raise is a default rather than a limit.

When a result hits its budget the message says so — a truncated file is never presented as a complete one:

read 1048576 bytes from big.log (truncated at its byte budget)

Output also passes a redaction pass on the way out: recognisable credential shapes (tokens with a documented prefix, private-key blocks, JWTs) are replaced with a marker. This is defence in depth, not data-loss prevention — a secret with no recognisable shape passes it. What actually keeps credentials out of results is the protected-path list above.

Auth-free capabilities

The files provider is the right place to start with capabilities — no token, no network, no external state:

ls ~/.engineering-runtime/capabilities/files/

runtime capability validate files/notes-roundtrip
runtime capability execute files/notes-roundtrip \
  --input path=./hello.txt --input message="first run"

The shipped examples demonstrate the patterns:

Capability Demonstrates
notes-roundtrip write → read
log-rotate append + list
scaffold-service-docs multi-write into a directory, then list
incident-log-lifecycle write → append → read
directory-snapshot-report multi-directory list + write a report
estate-config-update YAML + Terraform + CODEOWNERS estate change
yaml-image-rollout keyed-sequence YAML selection
terraform-provider-upgrade selected provider/version constraints

In a capability:

workflow:
  - provider: files
    args: [write, "${path}", "${message}"]

  - provider: files
    args: [read, "${path}"]

Known limitations

Limitation Workaround
No mkdir operation. write fails when a parent directory is missing Create the path some other way first. In a GitHub workflow, seeding a placeholder file through the Contents API makes Git create the path components; locally, runtime command run git or an existing directory works
No encoding primitive. No base64 or hashing operation Write literal content. Where a platform API requires base64, embed the encoded string directly in the capability
delete denied by default Change policy deliberately, or use git rm
No regex or arbitrary script transform Use exact text or a registered typed adapter
No automatic clone, Git stage/commit/push, PR, formatter or linter Compose those as separate governed capability steps
No cross-filesystem atomic transaction Use preflight, per-file atomic replacement and audited rollback states

These limits keep File Engine deterministic. A missing transformation is added as a typed adapter operation, never worked around with an unrestricted shell.

Operational examples on this site were verified against Runtime 0.9.8. After bootstrap, the version-exact files in Runtime Home win.