Provider Reference#

This document provides a reference for all built-in providers in scafctl.

Note: All YAML examples in this reference show only the relevant resolver or action snippet. To use them, place each snippet inside a complete solution file with apiVersion, kind, metadata, and spec sections. See the Getting Started tutorial for the full solution structure.

Overview#

Providers are execution primitives used by resolvers and actions. Each provider has capabilities that determine where it can be used:

CapabilityUsed InDescription
fromResolver resolve.withFetch or generate data
transformResolver transform.withTransform data
validationResolver validate.withValidate data
actionAction providerPerform side effects
authenticationHTTP authProvide authentication

Capabilities Matrix#

Providerfromtransformvalidationaction
cel
debug
directory
env
exec
file
git
github
go-template
hcl
http
identity
message
metadata
parameter
secret
sleep
static
validation

cel#

Transform and evaluate data using CEL (Common Expression Language) expressions.

Capabilities#

transform, action

Inputs#

FieldTypeRequiredDescription
expressionstringCEL expression to evaluate. Resolver data available under _
variablesanyAdditional variables for the CEL context

Output#

Returns the evaluation result (any type).

Examples#

# Transform: uppercase a string
transform:
  with:
    - provider: cel
      inputs:
        expression: "__self.toUpperCase()"

# Action: compute a value
provider: cel
inputs:
  expression: "_.items.map(i, i.price).sum()"

debug#

Debugging provider for inspecting resolver data during workflow execution.

Capabilities#

from, transform, validation, action

Inputs#

FieldTypeRequiredDescription
expressionstringCEL expression to filter/transform data before output
labelstringLabel or message for debug output context
formatstringOutput format: yaml, json, pretty (default: yaml)
destinationstringWhere to output: stdout, stderr, file (default: stdout)
pathstringFile path when destination is file
colorizeboolWhether to colorize terminal output

Examples#

# Debug all resolver data
resolve:
  with:
    - provider: debug
      inputs:
        label: "Resolver Context"

# Debug specific value
transform:
  with:
    - provider: debug
      inputs:
        expression: "_.config"
        format: json

directory#

Directory operations: listing contents with filtering, creating, removing, and copying directories.

Capabilities#

from, action

Inputs#

FieldTypeRequiredDescription
operationstringOperation: list, mkdir, rmdir, copy
pathstringTarget directory path (absolute or relative)
recursiveboolEnable recursive directory traversal (default: false)
maxDepthintMaximum recursion depth, 1–50 (default: 10)
includeContentboolRead and include file contents in output (default: false)
maxFileSizeintMaximum file size in bytes for content reading (default: 1048576)
filterGlobstringGlob pattern to filter entries (e.g., *.go). Mutually exclusive with filterRegex
filterRegexstringRegex to filter entry names. Mutually exclusive with filterGlob
excludeHiddenboolExclude hidden files/directories (names starting with .)
checksumstringCompute checksum for files: md5, sha256, sha512 (requires includeContent)
createDirsboolCreate parent directories for mkdir (like mkdir -p)
destinationstringDestination path for copy operation
forceboolForce removal of non-empty directories for rmdir

Output (list)#

FieldTypeDescription
entriesarrayList of directory entries
entries[].pathstringRelative path from the listed directory
entries[].absolutePathstringAbsolute filesystem path
entries[].namestringFile or directory name
entries[].extensionstringFile extension including dot (e.g., .go)
entries[].sizeintSize in bytes
entries[].isDirboolWhether entry is a directory
entries[].typestringEntry type: file or dir
entries[].modestringFile permission mode (e.g., 0644)
entries[].modTimestringLast modification time (RFC3339)
entries[].mimeTypestringMIME type based on extension
entries[].contentstringFile content (when includeContent is true)
entries[].contentEncodingstringtext or base64
entries[].checksumstringFile checksum (when checksum is specified)
entries[].checksumAlgorithmstringAlgorithm used
totalCountintTotal number of entries
dirCountintNumber of directories
fileCountintNumber of files
totalSizeintTotal size of all files in bytes
basePathstringAbsolute path of the listed directory

Output (mkdir, rmdir, copy)#

FieldTypeDescription
successboolWhether the operation succeeded
operationstringOperation that was performed
pathstringAbsolute path of the target directory

Examples#

# List directory contents
resolve:
  with:
    - provider: directory
      inputs:
        operation: list
        path: ./src

# Recursively find all Go files
resolve:
  with:
    - provider: directory
      inputs:
        operation: list
        path: ./pkg
        recursive: true
        filterGlob: "*.go"
        excludeHidden: true

# List with file contents and checksums
resolve:
  with:
    - provider: directory
      inputs:
        operation: list
        path: ./config
        recursive: true
        includeContent: true
        filterGlob: "*.yaml"
        checksum: sha256
        maxFileSize: 524288

# Create nested directory structure
provider: directory
inputs:
  operation: mkdir
  path: ./output/reports/2026
  createDirs: true

# Force-remove a directory
provider: directory
inputs:
  operation: rmdir
  path: ./tmp/build-output
  force: true

# Copy a directory tree
provider: directory
inputs:
  operation: copy
  path: ./config
  destination: ./config-backup

Security#

The directory provider enforces several security measures:

  • Recursion depth limit: The maxDepth input is clamped to 1–50 (default: 10). This prevents unbounded filesystem traversal that could cause resource exhaustion.
  • Symlink skipping: Symbolic links are silently skipped during listing to prevent symlink escape attacks where a symlink points outside the intended directory tree.
  • File size limit: When includeContent is enabled, maxFileSize (default: 1 MB) caps the content read per file to prevent memory exhaustion from large files.

env#

Read environment variables.

Capabilities#

from

Inputs#

FieldTypeRequiredDescription
operationstringOperation: get, list
namestringVariable name (required for get)
defaultstringDefault value if variable not set
prefixstringFilter variables by prefix (for list)

Output#

FieldTypeDescription
valuestringVariable value (for get)
variablesmapKey-value pairs (for list)
foundboolWhether the variable exists

Examples#

# Get environment variable with default
resolve:
  with:
    - provider: env
      inputs:
        operation: get
        name: DATABASE_URL
        default: "postgres://localhost/dev"

# List all vars with prefix
resolve:
  with:
    - provider: env
      inputs:
        operation: list
        prefix: "APP_"

Security#

The env provider enforces several security measures:

  • Mandatory prefix for list: The list operation requires a non-empty prefix input. Listing all environment variables without a scope would expose process secrets such as tokens, API keys, and database credentials.
  • No credential-bearing variables in output: By forcing a prefix scope, the provider prevents accidental enumeration of variables like AWS_SECRET_ACCESS_KEY, DATABASE_URL, or GITHUB_TOKEN that happen to exist in the process environment.

exec#

Execute shell commands using an embedded cross-platform POSIX shell interpreter. Commands work identically on Linux, macOS, and Windows without requiring external shell binaries. Supports pipes, redirections, variable expansion, command substitution, and common coreutils on all platforms. Optionally use external shells (bash, pwsh, cmd) for platform-specific features.

Capabilities#

from, transform, action

Inputs#

FieldTypeRequiredDefaultDescription
commandstringCommand to execute. Supports POSIX shell syntax including pipes, redirections, variable expansion, and command substitution by default
argsarrayAdditional arguments appended to the command. Arguments are automatically shell-quoted for safety
stdinstringStandard input to provide to the command
workingDirstringWorking directory for command execution
envobjectEnvironment variables to set (key-value pairs). Merged with the parent process environment
timeoutintTimeout in seconds (0 or omit for no timeout, max 3600)
shellstringautoShell interpreter to use: auto (embedded POSIX shell — works on all platforms), sh (alias for auto), bash (external bash), pwsh (external PowerShell Core), cmd (external cmd.exe — Windows only)

Output#

FieldTypeDescription
stdoutstringStandard output
stderrstringStandard error
exitCodeintExit code
successboolWhether command succeeded (exit code 0) — action capability only
commandstringThe full command that was executed
shellstringThe shell interpreter that was used

Shell Modes#

ValueDescriptionPlatform
autoEmbedded POSIX shell (default). Pure Go — no external shell binary required. Supports pipes, redirections, variable expansion, command substitution, and Go-native coreutils on Windows.All
shAlias for autoAll
bashExternal bash binary from $PATH. Use for bash-specific features (globstar, arrays, etc.)Linux, macOS
pwshExternal PowerShell Core from $PATH. Use for PowerShell cmdlets and Windows administrationAll (where pwsh is installed)
cmdExternal cmd.exe. Use for Windows batch commandsWindows

Examples#

# Simple command — pipes and shell features work by default
provider: exec
inputs:
  command: "echo 'Hello, World!'"

# Command with arguments (automatically shell-quoted)
provider: exec
inputs:
  command: "echo"
  args: ["Hello", "World"]

# Shell pipeline — works on all platforms
provider: exec
inputs:
  command: "echo 'hello world' | tr a-z A-Z"

# With environment variables and working directory
provider: exec
inputs:
  command: "./deploy.sh"
  workingDir: "/opt/app"
  env:
    ENVIRONMENT: production
  timeout: 300

# PowerShell command
provider: exec
inputs:
  command: "Get-ChildItem | Select-Object Name"
  shell: pwsh

# External bash for bash-specific features
provider: exec
inputs:
  command: "shopt -s globstar; echo **/*.go"
  shell: bash

Security#

The exec provider enforces several security measures:

  • Lint rule for command injection: The exec-command-injection lint rule emits a warning when the command input uses a dynamic expression (expr) or template (tmpl). Shell metacharacters in resolved values can escape the intended command and execute arbitrary code.
  • Shell-quoted arguments: The args input automatically shell-quotes each argument before appending it to the command. Always pass dynamic/user-controlled data via args instead of interpolating it into the command string.
# BAD — dynamic value in command string risks injection
provider: exec
inputs:
  command:
    expr: "'echo ' + _.userInput"

# GOOD — static command, dynamic value via args (shell-quoted)
provider: exec
inputs:
  command: echo
  args:
    - expr: "_.userInput"

file#

Filesystem operations: read, write, check existence, delete, and batch write a tree of files.

Capabilities#

from, transform, action

Inputs#

FieldTypeRequiredDescription
operationstringOperation: read, write, exists, delete, write-tree
pathstringFile path — required for read, write, exists, delete
contentstringContent to write (required for write)
createDirsboolCreate parent directories if missing (for write)
encodingstringFile encoding: utf-8, binary (default: utf-8)
basePathstringDestination root directory (required for write-tree)
entriesarrayArray of {path, content} objects (required for write-tree)
outputPathstringGo template to transform each entry’s path before writing (write-tree only). Available variables: __filePath, __fileName, __fileStem, __fileExtension, __fileDir. Sprig functions supported.

Output#

FieldTypeDescription
contentstringFile content (for read)
existsboolWhether file exists
sizeintFile size in bytes
successboolOperation success (action only)
operationstringOperation performed (write-tree only)
basePathstringResolved base path (write-tree only)
filesWrittenintNumber of files written (write-tree only)
pathsarrayRelative paths of files written (write-tree only)

Examples#

# Read file
resolve:
  with:
    - provider: file
      inputs:
        operation: read
        path: "./config.json"

# Write file
provider: file
inputs:
  operation: write
  path: "./output/result.txt"
  content:
    expr: "_.processedData"
  createDirs: true

# Check if file exists
resolve:
  with:
    - provider: file
      inputs:
        operation: exists
        path: "./optional-config.yaml"

# Write a tree of rendered files, stripping .tpl extensions
provider: file
inputs:
  operation: write-tree
  basePath: ./output
  entries:
    rslvr: rendered
  outputPath: >-
    {{ if .__fileDir }}{{ .__fileDir }}/{{ end }}{{ .__fileStem }}

Security#

The file provider enforces several security measures:

  • Path traversal protection: The write-tree operation validates that every output file resolves to a path inside the declared basePath. Entries containing .. segments that escape the base directory are rejected with an error.
  • outputPath traversal protection: When outputPath is specified, the final templated path is also validated to remain within basePath. This prevents a crafted Go template from writing files outside the intended output directory.

git#

Git version control operations.

Capabilities#

from, action

Inputs#

FieldTypeRequiredDescription
operationstringOperation: clone, pull, status, add, commit, push, checkout, branch, log, tag
urlstringRepository URL (for clone)
pathstringLocal repository path
branchstringBranch name
messagestringCommit message
filesarrayFiles to add
tagstringTag name
remotestringRemote name (default: origin)
depthintClone depth for shallow clone
usernamestringUsername for authentication
passwordstringPassword/token (secret)
forceboolForce the operation

Examples#

# Clone repository
provider: git
inputs:
  operation: clone
  url: https://github.com/org/repo.git
  path: ./repo
  depth: 1

# Commit and push
provider: git
inputs:
  operation: commit
  path: ./repo
  message:
    expr: "'Release ' + _.version"

# Then push
provider: git
inputs:
  operation: push
  path: ./repo

Security#

The git provider enforces several security measures:

  • Credential isolation: Authentication credentials are passed via a temporary .netrc file with 0600 permissions instead of being embedded in command-line arguments. This prevents credentials from being exposed via ps, /proc, or audit logs.
  • Netrc injection prevention: Username and password inputs are validated to reject whitespace and control characters (<= 0x20 or 0x7f). The netrc format is whitespace-delimited, so embedded spaces, tabs, or newlines could inject additional machine entries.
  • Automatic cleanup: The temporary credential directory is automatically removed after the git operation completes.
  • Terminal prompt disabled: GIT_TERMINAL_PROMPT=0 is set to prevent git from hanging on interactive credential prompts in automated environments.

github#

Interact with GitHub via GraphQL (reads, issues, PRs, signed commits, branches, tags) and REST (releases). Uses the configured GitHub auth handler automatically. Commit operations use createCommitOnBranch for GPG-signed multi-file atomic commits.

For arbitrary HTTP requests to GitHub, use the http provider with auth: github.

Capabilities#

from, transform, action

Inputs#

FieldTypeRequiredDescription
operationstringAPI operation (see operations table below)
ownerstringRepository owner (user or organization)
repostringRepository name
api_basestringGitHub API base URL (default: https://api.github.com). Set for GitHub Enterprise.
pathstringFile path within the repository (for get_file)
refstringGit reference (branch, tag, or commit SHA). Defaults to repo’s default branch
numberintIssue or pull request number
statestringFilter by state: open, closed, all, merged (default: open)
per_pageintResults per page, 1–100 (default: 30)
titlestringTitle for issue or pull request create/update
bodystringBody text for issue, pull request, release, or comment
labelsarrayLabels to apply (names for issues)
assigneesarrayAssignee login usernames
headstringHead branch for creating a pull request
basestringBase branch for creating a pull request
draftboolCreate PR as draft
merge_methodstringMerge method: MERGE, SQUASH, REBASE (default: MERGE)
branchstringBranch name for commit/branch/tag operations
messagestringCommit message headline
expected_head_oidstringExpected HEAD OID (40-char SHA) for optimistic locking in create_commit
additionsarrayFiles to add/update: [{path, content}]
deletionsarrayFiles to delete: [{path}]
oidstringGit object ID (SHA) for branch/tag creation
tagstringTag name for tag operations
tag_namestringTag name for release
namestringRelease name/title
release_idintRelease ID for update/delete
prereleaseboolWhether release is a prerelease
target_commitishstringBranch/SHA target for release tag

Operations#

Read operations (capabilities: from, transform):

OperationDescription
get_repoGet repository metadata
get_fileGet file content (returned as plain text, not base64)
list_releasesList releases
get_latest_releaseGet the latest release
list_pull_requestsList pull requests (filterable by state)
get_pull_requestGet a single pull request
list_issuesList issues (filterable by state)
get_issueGet a single issue
list_issue_commentsList comments on an issue
list_branchesList branches
get_branchGet a single branch
list_tagsList tags
get_head_oidGet HEAD commit SHA for a branch

Write operations (capability: action — returns success boolean):

OperationAPIDescription
create_issueGraphQLCreate a new issue
update_issueGraphQLUpdate an existing issue
create_issue_commentGraphQLAdd a comment to an issue
create_pull_requestGraphQLOpen a new pull request
update_pull_requestGraphQLUpdate a pull request
merge_pull_requestGraphQLMerge a pull request
close_pull_requestGraphQLClose a pull request
create_commitGraphQLCreate a GPG-signed commit (multi-file, atomic)
create_branchGraphQLCreate a branch
delete_branchGraphQLDelete a branch
create_tagGraphQLCreate a lightweight tag
delete_tagGraphQLDelete a tag
create_releaseRESTCreate a release
update_releaseRESTUpdate a release
delete_releaseRESTDelete a release

Output#

Read operations:

FieldTypeDescription
resultanyAPI response — structure varies by operation

Write operations (action):

FieldTypeDescription
successboolWhether the operation succeeded
operationstringThe operation that was performed
resultanyAPI response data
errorstringError message if the operation failed

Examples#

# Get repository info
resolve:
  with:
    - provider: github
      inputs:
        operation: get_repo
        owner: octocat
        repo: hello-world

# Get file content from a specific branch (returns plain text)
transform:
  with:
    - provider: github
      inputs:
        operation: get_file
        owner: octocat
        repo: hello-world
        path: README.md
        ref: main

# Create a GPG-signed commit with multiple files
action:
  with:
    - provider: github
      inputs:
        operation: create_commit
        owner: my-org
        repo: my-repo
        branch: feature-branch
        message: "feat: add scaffolded files"
        expected_head_oid: abc123def456789012345678901234567890abcd
        additions:
          - path: src/main.go
            content: "package main\n\nfunc main() {}\n"
          - path: README.md
            content: "# My Project\n"

# Create an issue with labels
action:
  with:
    - provider: github
      inputs:
        operation: create_issue
        owner: my-org
        repo: my-repo
        title: "Bug: something is broken"
        body: "Steps to reproduce..."
        labels:
          - bug
          - priority/high

# Create a release (uses REST API)
action:
  with:
    - provider: github
      inputs:
        operation: create_release
        owner: my-org
        repo: my-repo
        tag_name: v1.0.0
        name: "Release 1.0.0"
        body: "First stable release"

go-template#

Transform data using Go text/template syntax. Supports single-template rendering (render, the default) and batch directory rendering (render-tree).

Capabilities#

transform, action

Inputs#

FieldTypeRequiredDescription
operationstringOperation: render (default) or render-tree
templatestringGo template content (required for render)
namestringTemplate name for error messages (defaults to "render-tree" for render-tree)
entriesarrayArray of {path, content} objects to render (required for render-tree)
missingKeystringBehavior for missing keys: default, zero, error
leftDelimstringLeft delimiter (default: {{)
rightDelimstringRight delimiter (default: }})
dataanyAdditional data to merge with resolver context
ignoredBlocksarrayBlocks to pass through literally without template processing. Each entry uses EITHER { start, end } markers (multi-line) OR { line } marker (single-line). Content is preserved as-is.

Output#

render operation: Returns the rendered template as a string.

render-tree operation: Returns an array of {path, content} objects where each content is the rendered result. Metadata includes templateName and entryCount.

Ignored Blocks#

Use ignoredBlocks to bypass template rendering for specific sections. This is useful when templates contain syntax that conflicts with Go template delimiters (e.g., Terraform ${}, Helm {{ }}, GitHub Actions ${{ }}).

Start/End Mode (Multi-line)#

Define start and end markers. All content between matched markers (inclusive) is preserved:

transform:
  with:
    - provider: go-template
      inputs:
        name: terraform-config
        template: |
          resource "aws_instance" "main" {
            ami           = "{{ .ami }}"
            instance_type = "{{ .instanceType }}"
            /*scafctl:ignore:start*/
            tags = {
              Name = "${var.name}"
            }
            /*scafctl:ignore:end*/
          }
        ignoredBlocks:
          - start: "/*scafctl:ignore:start*/"
            end: "/*scafctl:ignore:end*/"

Line Mode (Single-line)#

Define a line marker. Every line containing that substring is preserved literally:

transform:
  with:
    - provider: go-template
      inputs:
        name: workflow-config
        template: |
          name: Deploy {{ .appName }}
          steps:
            - run: echo ${{ secrets.TOKEN }}  # scafctl:ignore
            - run: echo "deployed"
        ignoredBlocks:
          - line: "# scafctl:ignore"

Note: line and start/end are mutually exclusive within a single entry, but different entries can use different modes.

The content between start and end markers (including the markers themselves) passes through unchanged. For line mode, the entire line containing the marker is preserved.

Examples#

# Render a single template
transform:
  with:
    - provider: go-template
      inputs:
        name: config
        template: |
          server:
            host: {{ .host }}
            port: {{ .port }}
            env: {{ .environment }}

# Batch-render a directory of templates (render-tree)
resolve:
  with:
    - provider: go-template
      inputs:
        operation: render-tree
        entries:
          expr: '_.templateFiles.entries'
        data:
          rslvr: vars

hcl#

Process HCL (HashiCorp Configuration Language) content. Supports four operations: parse (default) extracts structured block information; format canonically formats; validate checks syntax; generate produces HCL from structured input. Accepts single files, multiple paths, or a directory of .tf files.

Capabilities#

from, transform

Inputs#

FieldTypeRequiredDescription
operationstringparse (default), format, validate, or generate
contentstringRaw HCL content to process
pathstringPath to a single HCL file
pathsarrayArray of HCL file paths (merged for parse; per-file for format/validate)
dirstringDirectory path — all .tf/.tf.json files are processed
blocksobjectStructured block data for generate (same schema as parse output)
output_formatstringGeneration output format: hcl (default) or json (Terraform JSON syntax .tf.json)

Source selection: For parse/format/validate, provide exactly one of content, path, paths, or dir (mutually exclusive). For generate, use blocks and optionally output_format.

Output — parse (default)#

FieldTypeDescription
variablesarrayVariable blocks (name, type, default, description, sensitive, validation)
resourcesarrayResource blocks (type, name, attributes, sub-blocks)
dataarrayData source blocks (type, name, attributes, sub-blocks)
modulesarrayModule blocks (name, source, version, attributes)
outputsarrayOutput blocks (name, value, description, sensitive)
localsmapLocal values merged across all locals blocks
providersarrayProvider configuration blocks (name, alias, attributes)
terraformobjectTerraform block (required_version, required_providers, backend, cloud)
movedarrayMoved blocks (from, to)
importarrayImport blocks (to, id, provider)
checkarrayCheck blocks (name, data, assertions)

When multiple files are parsed (paths or dir), results are merged: arrays are concatenated, locals and terraform maps are merged (last-file-wins for conflicting keys).

Output — format#

FieldTypeDescription
formattedstringThe canonically formatted HCL content (single file)
changedbooltrue if the formatter modified the content

Multi-file format returns { files: [{filename, formatted, changed}, ...], changed: bool }.

Output — validate#

FieldTypeDescription
validbooltrue if no syntax errors were found
error_countintNumber of error-level diagnostics
diagnosticsarrayDiagnostic entries with severity, summary, detail, range

Multi-file validate returns { valid: bool, error_count: int, files: [{filename, valid, error_count, diagnostics}, ...] }.

Output — generate#

FieldTypeDescription
hclstringGenerated HCL text (native HCL syntax or Terraform JSON depending on output_format)

Metadata includes output_format (hcl or json) indicating which format was produced.

Examples#

# Parse inline HCL content (operation defaults to "parse")
resolve:
  with:
    - provider: hcl
      inputs:
        content: |
          variable "region" {
            type    = string
            default = "us-east-1"
          }

# Parse an HCL file
resolve:
  with:
    - provider: hcl
      inputs:
        path: ./main.tf

# Parse all .tf files in a directory (results merged)
resolve:
  with:
    - provider: hcl
      inputs:
        dir: ./terraform

# Parse multiple specific files
resolve:
  with:
    - provider: hcl
      inputs:
        paths:
          - ./main.tf
          - ./variables.tf
          - ./outputs.tf

# Transform: parse HCL from another resolver's output
transform:
  with:
    - provider: hcl
      inputs:
        content: "{{ .resolvers.tfFile.content }}"

# Format inline HCL content
resolve:
  with:
    - provider: hcl
      inputs:
        operation: format
        content: |
          variable "region" {
          type=string
          default="us-east-1"
          }

# Format all files in a directory
resolve:
  with:
    - provider: hcl
      inputs:
        operation: format
        dir: ./terraform

# Validate HCL syntax
resolve:
  with:
    - provider: hcl
      inputs:
        operation: validate
        path: ./main.tf

# Generate HCL from structured data
resolve:
  with:
    - provider: hcl
      inputs:
        operation: generate
        blocks:
          variables:
            - name: region
              type: string
              default: us-east-1
              description: "AWS region"

# Generate Terraform JSON (.tf.json) from structured data
resolve:
  with:
    - provider: hcl
      inputs:
        operation: generate
        output_format: json
        blocks:
          variables:
            - name: region
              type: string
              default: us-east-1
          resources:
            - type: aws_instance
              name: web
              attributes:
                ami: ami-12345
                instance_type: t3.micro

http#

HTTP client for API calls with built-in pagination support for fetching data across multiple pages.

Capabilities#

from, transform, action

Inputs#

FieldTypeRequiredDescription
urlstringURL to request
methodstringHTTP method (default: GET)
headersobjectHTTP headers
bodystringRequest body
timeoutintTimeout in seconds (max 300)
retryobjectRetry configuration
authstringAuth provider (e.g., entra, github)
scopestringOAuth scope for authentication
paginationobjectPagination configuration (see below)
autoParseJsonboolParse response body as JSON when Content-Type is application/json. Enables direct field access (e.g., _.result.body.items)
pollobjectPolling configuration — re-execute request until a condition is met (see below)

Pagination#

The pagination input enables automatic multi-page fetching. Five strategies are supported to cover different API pagination patterns.

Pagination Fields#

FieldTypeRequiredDescription
strategystringOne of: offset, pageNumber, cursor, linkHeader, custom
maxPagesintSafety limit for max pages to fetch (default: 100, max: 10000)
collectPathstringCEL expression to extract items from each response (e.g., body.items)
stopWhenstringCEL expression; if true, stop paginating (e.g., size(body.items) == 0)

CEL variables available in collectPath, stopWhen, and strategy-specific expressions:

VariableTypeDescription
statusCodeintHTTP response status code
bodyanyParsed JSON response body
rawBodystringRaw response body string
headersobjectResponse headers
pageintCurrent page number (1-based)

Strategy: offset#

Increments an offset query parameter each page.

FieldTypeDefaultDescription
limitint(required)Page size
offsetParamstringoffsetQuery parameter name for offset
limitParamstringlimitQuery parameter name for limit

Strategy: pageNumber#

Increments a page number query parameter each page.

FieldTypeDefaultDescription
pageSizeint(required)Page size
pageParamstringpageQuery parameter name for page number
pageSizeParamstringpageSizeQuery parameter name for page size
startPageint1Starting page number

Strategy: cursor#

Extracts a cursor token or next URL from the response to fetch subsequent pages.

FieldTypeDescription
nextTokenPathstringCEL expression to extract cursor from response (e.g., body.nextCursor)
nextTokenParamstringQuery parameter to set with the cursor value (required with nextTokenPath)
nextURLPathstringCEL expression to extract the full next page URL (e.g., body['@odata.nextLink']). Alternative to nextTokenPath.

Use nextTokenPath + nextTokenParam for APIs that return a token. Use nextURLPath for APIs that return a full URL (e.g., Microsoft Graph @odata.nextLink).

Strategy: linkHeader#

Follows rel="next" links in the Link response header (RFC 8288). Used by GitHub, GitLab, and other REST APIs. No additional configuration needed.

Strategy: custom#

Full control using CEL expressions.

FieldTypeDescription
nextURLstringCEL expression returning the full next page URL (empty string = stop)
nextParamsstringCEL expression returning a map of query params for the next request (empty map = stop)

Polling#

The poll input enables re-executing the request until a response condition is met. This is different from retry (which handles transient failures) — polling re-executes on successful responses until the content matches expectations.

FieldTypeRequiredDescription
untilstringCEL expression evaluated against the response. Polling stops when this returns true. Available variables: body (parsed if JSON), statusCode, headers
failWhenstringCEL expression that triggers immediate failure (e.g., terminal error states)
intervalstringDuration between polls (default: 5s). Format: 1s, 30s, 2m
maxAttemptsintMaximum number of poll attempts (default: 60)
# Wait for deployment to complete
resolve:
  with:
    - provider: http
      inputs:
        url: https://api.example.com/deployments/123/status
        method: GET
        auth: entra
        autoParseJson: true
        poll:
          until: 'body.status == "succeeded"'
          failWhen: 'body.status == "failed"'
          interval: 10s
          maxAttempts: 30

Output#

FieldTypeDescription
statusCodeintHTTP status code (last page when paginating)
bodyanyResponse body as string, or parsed JSON object when autoParseJson: true. When paginating with collectPath, contains JSON array of all collected items
headersobjectResponse headers (last page when paginating)
successboolWhether request succeeded (action only)
pagesintNumber of pages fetched (only when paginating)
totalItemsintTotal items collected across all pages (only when paginating)

Examples#

# GET request
resolve:
  with:
    - provider: http
      inputs:
        url: https://api.example.com/config
        headers:
          Accept: application/json

# POST with body
provider: http
inputs:
  url: https://api.example.com/deploy
  method: POST
  headers:
    Content-Type: application/json
  body:
    expr: 'toJson({"image": _.image, "env": _.environment})'
  timeout: 60

# With retry
provider: http
inputs:
  url: https://api.example.com/status
  retry:
    maxAttempts: 3
    backoff: exponential
    initialDelay: 1s

# Authenticated GitHub API request
resolve:
  with:
    - provider: http
      inputs:
        url: https://api.github.com/user/repos
        headers:
          Accept: application/json
        auth: github
        scope: repo

# Cursor pagination (token-based)
provider: http
inputs:
  url: https://api.example.com/items
  pagination:
    strategy: cursor
    maxPages: 10
    nextTokenPath: "body.nextCursor"
    nextTokenParam: "cursor"
    collectPath: "body.items"
    stopWhen: "body.nextCursor == null"

# Cursor pagination (OData / Microsoft Graph nextLink)
provider: http
inputs:
  url: https://graph.microsoft.com/v1.0/users?$top=100
  authProvider: entra
  scope: "https://graph.microsoft.com/.default"
  pagination:
    strategy: cursor
    maxPages: 50
    nextURLPath: "body['@odata.nextLink']"
    collectPath: "body.value"

# Link header pagination (GitHub-style)
provider: http
inputs:
  url: https://api.github.com/users/octocat/repos?per_page=30
  headers:
    Accept: application/vnd.github+json
  pagination:
    strategy: linkHeader
    maxPages: 5
    collectPath: "body"

# Offset pagination
provider: http
inputs:
  url: https://api.example.com/records
  pagination:
    strategy: offset
    maxPages: 20
    limit: 50
    collectPath: "body.records"
    stopWhen: "size(body.records) < 50"

# Page number pagination
provider: http
inputs:
  url: https://api.example.com/products
  pagination:
    strategy: pageNumber
    maxPages: 10
    pageSize: 25
    pageParam: "page"
    pageSizeParam: "per_page"
    collectPath: "body.products"
    stopWhen: "size(body.products) == 0"

# Custom pagination with CEL expressions
provider: http
inputs:
  url: https://api.example.com/search?q=test
  pagination:
    strategy: custom
    maxPages: 10
    nextURL: "has(body.links) && has(body.links.next) ? body.links.next : ''"
    collectPath: "body.results"
    stopWhen: "!has(body.links) || !has(body.links.next)"

Security#

The HTTP provider enforces several security measures:

  • SSRF protection: Requests to private, loopback, and link-local IP addresses (e.g., 169.254.169.254) are blocked by default. Set httpClient.allowPrivateIPs: true in config to allow private network access for on-premises endpoints.
  • Response body size limit: Each response is limited to httpClient.maxResponseBodySize (default: 100 MB). This prevents denial-of-service via unbounded responses from malicious or misconfigured servers. Applies to both direct requests and each page in paginated requests.
  • Redirect validation: Each redirect target is checked against the SSRF private IP blocklist. A maximum of 10 redirects is enforced.
  • Pagination host validation: Pagination next URLs must stay on the same hostname as the original request to prevent open redirect attacks.
  • Token security: Authentication tokens are injected via the Authorization header and are never logged. Token refresh on 401 responses is handled transparently.
# Override the default response body size limit (e.g. for large API responses)
# In config.yaml:
httpClient:
  maxResponseBodySize: 209715200  # 200 MB

identity#

Get authentication identity information without exposing tokens. Supports reading stored session metadata or minting a fresh scoped access token to inspect its claims on demand.

Capabilities#

from

Inputs#

FieldTypeRequiredDescription
operationstringOperation: status, claims, groups, list
handlerstringAuth handler name (e.g., entra, github)
scopestringOAuth scope for on-demand token minting. When set, claims and status mint a fresh access token for the scope and return its details instead of stored session metadata. Not supported for groups or list.

Operations#

OperationDescription
statusReturns authentication status, expiry, and identity type from stored session metadata (or scoped token when scope is set)
claimsReturns identity claims (name, email, tenant, etc.) from stored session metadata (or scoped token JWT when scope is set)
groupsReturns Entra group memberships for the authenticated user
listLists all registered auth handler names

Output#

FieldTypeDescription
operationstringThe operation that was executed
handlerstringThe auth handler that was used
authenticatedboolWhether authenticated
identityTypestringIdentity type: user or service-principal
claimsobjectToken claims (email, name, subject, tenantId, etc.)
tenantIdstringTenant ID (for Entra)
expiresAtstringToken expiration in RFC3339 format
expiresInstringHuman-readable duration until expiry
groupsarrayGroup display names (for groups operation)
handlersarrayAvailable handler names (for list operation)
scopedTokenbooltrue when the response was derived from a scoped access token
tokenScopestringThe OAuth scope the token was minted for (when scope input was set)
tokenTypestringToken type, typically Bearer (when scope input was set)
flowstringAuth flow that produced the token (when scope input was set)
sessionIdstringStable session identifier (when scope input was set)

Opaque tokens: When scope is provided and the access token is not a decodable JWT (e.g., encrypted Microsoft Graph tokens), claims will be null and a warning is added to the output. Token metadata (expiry, type) is still returned where available.

Examples#

# Check if authenticated (Entra)
resolve:
  with:
    - provider: identity
      inputs:
        operation: status
        handler: entra

# Check if authenticated (GitHub)
resolve:
  with:
    - provider: identity
      inputs:
        operation: status
        handler: github

# Get claims from stored session metadata
resolve:
  with:
    - provider: identity
      inputs:
        operation: claims
        handler: entra

# Get GitHub claims (login, name, email)
resolve:
  with:
    - provider: identity
      inputs:
        operation: claims
        handler: github

# Mint a scoped token and inspect its claims
resolve:
  with:
    - provider: identity
      inputs:
        operation: claims
        scope: api://my-app/.default

# Check scoped token status (expiry, flow, tokenType)
resolve:
  with:
    - provider: identity
      inputs:
        operation: status
        scope: https://management.azure.com/.default
        handler: entra

# List all registered auth handlers
resolve:
  with:
    - provider: identity
      inputs:
        operation: list

# Get Entra group memberships
resolve:
  with:
    - provider: identity
      inputs:
        operation: groups
        handler: entra

metadata#

Returns runtime metadata about the scafctl process and the currently-executing solution. Requires no inputs — all data is gathered from the execution context and process environment. Useful for conditional logic, auditing, and passing runtime info to templates and downstream resolvers.

Capabilities#

from

Inputs#

None. The metadata provider accepts no inputs.

Output#

FieldTypeDescription
versionobjectBuild version information
version.buildVersionstringSemantic version of the scafctl build
version.commitstringGit commit hash of the build
version.buildTimestringTimestamp of the build
argsstring[]Command-line arguments passed to scafctl
cwdstringCurrent working directory
entrypointstringHow scafctl was invoked: "cli", "api", or "unknown"
commandstringThe command path (e.g. scafctl/run/solution)
solutionobjectMetadata about the currently-running solution
solution.namestringSolution name
solution.versionstringSolution version
solution.displayNamestringSolution display name
solution.descriptionstringSolution description
solution.categorystringSolution category
solution.tagsstring[]Solution tags

Examples#

Resolve runtime metadata:

resolvers:
  runtime-meta:
    resolve:
      with:
        - provider: metadata

Use metadata in a downstream resolver via CEL:

resolvers:
  runtime-meta:
    resolve:
      with:
        - provider: metadata
  greeting:
    dependsOn: [runtime-meta]
    resolve:
      with:
        - provider: cel
          inputs:
            expression: >-
              "Running " + _.runtime_meta.solution.name +
              " v" + _.runtime_meta.solution.version +
              " via " + _.runtime_meta.entrypoint

message#

Outputs styled terminal messages with built-in types, custom formatting via lipgloss, destination control, and respects --quiet and --no-color flags. For dynamic interpolation, use the framework’s tmpl: or expr: ValueRef on the message input — the provider does not handle templating internally.

Capabilities#

action

Inputs#

FieldTypeRequiredDescription
messagestringMessage text to output. Use tmpl: or expr: ValueRef for dynamic interpolation.
typestringMessage type: success, warning, error, info (default), debug, plain
labelstringContextual prefix rendered as dimmed [label] between icon and message (e.g., step 2/5)
styleobjectCustom formatting that merges on top of type defaults: color (hex or named), bold, italic, icon
destinationstringOutput target: stdout (default) or stderr
newlineboolAppend trailing newline (default: true)

Output#

FieldTypeDescription
successboolAlways true on success
messagestringRendered message text

Examples#

Built-in type styling:

resolvers:
  step1:
    resolve:
      with:
        - provider: message
          inputs:
            message: "Build succeeded"
            type: success

Custom style with icon:

resolvers:
  deploy:
    resolve:
      with:
        - provider: message
          inputs:
            message: "Starting pipeline"
            style:
              color: "#FF5733"
              bold: true
              icon: "\U0001F680"

Go template interpolation via tmpl: ValueRef:

resolvers:
  config:
    resolve:
      with:
        - provider: static
          inputs:
            value:
              appName: my-service
              version: 2.0.0
  deploy-msg:
    resolve:
      with:
        - provider: message
          inputs:
            message:
              tmpl: "Deploying {{ .config.appName }} v{{ .config.version }}"
            type: info

CEL expression via expr: ValueRef:

resolvers:
  items:
    resolve:
      with:
        - provider: static
          inputs:
            value: [a, b, c]
  status:
    resolve:
      with:
        - provider: message
          inputs:
            message:
              expr: "'Processed ' + string(size(_.items)) + ' items'"
            type: success

parameter#

Access CLI parameters passed via -r flags.

Capabilities#

from

Inputs#

FieldTypeRequiredDescription
keystringParameter name

Output#

FieldTypeDescription
valueanyParameter value
foundboolWhether parameter was provided
typestringDetected type of value

Examples#

# Get parameter with fallback
resolve:
  with:
    - provider: parameter
      inputs:
        key: environment
    - provider: static
      inputs:
        value: "dev"  # Default if not provided

Usage:

scafctl run solution -f sol.yaml -r environment=production
scafctl run solution -f sol.yaml -r environment=production

secret#

Retrieve encrypted secrets from the scafctl secrets store.

Capabilities#

from

Inputs#

FieldTypeRequiredDescription
operationstringOperation: get or list
namestringSecret name (for get)
patternstringRegex pattern to match names
requiredboolError if not found
defaultstringValue when not found

Examples#

# Get secret
resolve:
  with:
    - provider: secret
      inputs:
        operation: get
        name: api-key
        required: true

# Get with default
resolve:
  with:
    - provider: secret
      inputs:
        operation: get
        name: optional-key
        default: "fallback-value"

Manage secrets via CLI:

scafctl secrets set api-key "my-secret-value"
scafctl secrets list
scafctl secrets set api-key "my-secret-value"
scafctl secrets list

sleep#

Pause execution for a specified duration.

Capabilities#

from, transform, validation, action

Inputs#

FieldTypeRequiredDescription
durationstringDuration (Go format: 1s, 500ms, 2m)

Examples#

# Wait between API calls
provider: sleep
inputs:
  duration: "2s"

static#

Return a constant value. Useful for defaults and fallbacks.

Capabilities#

from, transform

Inputs#

FieldTypeRequiredDescription
valueanyStatic value to return

Examples#

# Default fallback
resolve:
  with:
    - provider: env
      inputs:
        operation: get
        name: CONFIG_PATH
    - provider: static
      inputs:
        value: "/etc/app/config.yaml"

# Complex default
resolve:
  with:
    - provider: static
      inputs:
        value:
          timeout: 30
          retries: 3
          endpoints:
            - https://primary.example.com
            - https://backup.example.com

validation#

Validate data using regex patterns and CEL expressions.

Capabilities#

transform, validation

Inputs#

FieldTypeRequiredDescription
valuestringValue to validate (uses __self in transform context)
matchstringRegex pattern that must match
notMatchstringRegex pattern that must NOT match
expressionstringCEL expression that must be true
messagestringCustom error message on failure

Output#

FieldTypeDescription
validboolWhether validation passed
errorsarrayValidation error messages
detailsstringFailure details

Examples#

# Validate with regex
validate:
  with:
    - provider: validation
      inputs:
        match: "^[a-z][a-z0-9-]+$"
        message: "Name must be lowercase alphanumeric with dashes"

# Validate with CEL
validate:
  with:
    - provider: validation
      inputs:
        expression: "__self in ['dev', 'staging', 'prod']"
        message: "Environment must be dev, staging, or prod"

# Combined validation
validate:
  with:
    - provider: validation
      inputs:
        match: "^v[0-9]+\\.[0-9]+\\.[0-9]+$"
        expression: "!__self.startsWith('v0.')"
        message: "Version must be semver format and >= v1.0.0"

Next Steps#