Skip to main content

Configuration

.hephconfig sits at the root of your workspace and marks it as a heph project. It registers the plugins your build uses and tunes a handful of engine-level settings. It is YAML.

.hephconfig
version: 1.2.3
plugins:
- builtin: buildfile
- builtin: bash

Pinning the version

.hephconfig
version: v1.2.3

version pins the heph release for this workspace so every machine and CI job runs the same binary. The tag names a release in a release channeldev by default, stable for slower-moving pins. When the running binary differs from the pin, heph automatically downloads the pinned release and re-execs into it on startup — the rest of the run is served by the pinned version. The downloaded binary is cached in ~/.heph/versions/<tag>/ and reused on subsequent runs.

To disable automatic self-upgrade for a process tree, set HEPH_NO_SELF_UPDATE to any non-empty value:

export HEPH_NO_SELF_UPDATE=1
heph run //...

This is useful in sandboxed environments or where you manage toolchains yourself.

Pinning a release flavour

Each release publishes two flavours of the heph binary:

  • std — the default. Small; stripped of debug symbols.
  • debug — same behavior, but keeps symbols so in-process backtraces (--diag-backtrace, panic backtraces) resolve to function names and file/line instead of raw addresses.

versionFlavour selects which one self-upgrade downloads:

.hephconfig
version: v1.2.3
versionFlavour: debug

Changing versionFlavour alone — without bumping version — is enough to trigger a re-download on the next run. heph version reports the running flavour as part of the build metadata (e.g. v1.2.3+gcf0045d9.debug); the std flavour reports nothing extra.

Keys

Every key below is optional.

KeyTypeDefaultDescription
versionstringunsetPins the heph release for this workspace. When set, heph automatically downloads and re-execs into the pinned version on startup. See Pinning the version and Release channels.
versionFlavourstring"" (std)Selects which release flavour self-upgrade downloads: "" for std, or debug for the unstripped build. See Pinning a release flavour.
pluginslist of plugin entries[]Plugins to register. Each entry sets exactly one of builtin, path, or url, plus an optional options map and, for url: entries, an optional checksum.
homeDirpathunsetWhere heph keeps its home and cache.
fs{skip: string[]}{}Workspace-wide ignore patterns shared with all tree-walking plugins.
memCache{perEntryBytes, capacityBytes}unsetIn-memory cache sizing.
cache{spillThresholdBytes: number}{}Durable local-cache tuning.
cachesmap of {uri, read, write, concurrency, endpoint, region}{}Named remote (shared) caches.
fuse{enabled: true | false | "auto"}offSandbox overlay mode.
lock{backend: fs | mem}fsExecute-phase lock backend.
telemetry{enabled: true | false}{} (enabled)Anonymous usage reporting. Set enabled: false to opt out.

Registering plugins

plugins is a list of entries. Each entry must set exactly one of:

KeyLoads
builtinA compiled-in plugin by name (e.g. buildfile, exec).
pathA local *-plugin.json manifest (relative to the workspace root, or absolute).
urlA remote *-plugin.json manifest — downloaded and cached in ~/.heph/plugins/.

Each entry also accepts an optional options map passed to that plugin:

.hephconfig
plugins:
- builtin: buildfile
options:
patterns: [BUILD, "*.BUILD"]
- builtin: exec
options:
path:
- /usr/local/bin
- /usr/bin
- /bin
- path: ./path/to/my-plugin.json
options:
some_option: value

url: entries also accept a checksum field to pin the downloaded manifest — see Pinning manifests with checksums.

The keys allowed inside options are defined by each plugin — see its page (for example Buildfile, Exec, Go). The engine only wires plugins together; it does not interpret their options.

note

Some plugins are built-in and always available (group, fs, hostbin, query); others must be listed here before a target can use them. Each plugin page states which.

External plugins (path: and url:)

An external plugin ships as a shared library (cdylib) paired with a *-plugin.json manifest. The manifest names the plugin and lists per-platform library artifacts; heph picks the right one for the current host.

path: points at a manifest on disk (relative to the workspace root, or absolute, or ~/-prefixed). url: fetches the manifest from a URL; the downloaded manifest and library are cached in ~/.heph/plugins/<os>-<arch>/ and reused on subsequent runs.

channel
.hephconfig
plugins:
# Local manifest on disk
- path: ./path/to/my-plugin.json

# Remote manifest — downloaded and cached automatically
- url: https://github.com/hephbuild/heph-artifacts-v1/releases/latest/download/heph-go-plugin.json

path: and url: plugins are supported on Unix only.

Pinning manifests with checksums

A url: entry accepts an optional checksum field that pins the SHA-256 of the downloaded manifest. heph verifies the manifest against this digest before trusting anything it declares — a mismatch is a hard error.

channel
.hephconfig
plugins:
- url: https://github.com/hephbuild/heph-artifacts-v1/releases/latest/download/heph-go-plugin.json
checksum: sha256:<hex>

The checksum is published alongside each manifest as a <manifest>.sha256 sidecar file (e.g. heph-go-plugin.json.sha256). Copy the value from that sidecar into checksum: to lock the manifest to a known-good digest.

The field anchors a trust chain: the config pins the manifest → the manifest pins each per-host artifact (cdylib) with its own checksum entry → the loaded binary is verified. Both levels must pass before any plugin code runs.

FormatMeaning
sha256:<hex>SHA-256 digest in lowercase hex. The only supported algorithm.

checksum is optional. Omitting it skips manifest verification; per-artifact checksums embedded in the manifest still apply when present. checksum is only valid for url: entries — builtin: and path: entries reject it.

fs — workspace ignore patterns

fs.skip is a list of workspace-root-relative patterns shared with every tree-walking plugin — the fs plugin, the buildfile provider, and the go provider. Patterns in this list apply everywhere at once, unlike per-plugin skip options which affect only one provider.

.hephconfig
fs:
skip:
- vendor # literal dir — pruned by every plugin
- "./node_modules" # leading ./ is normalized (same as "node_modules")
- "**/generated/**" # glob — prunes any dir named generated at any depth

Each entry is interpreted by its shape:

Entry shapeEffect
Literal path (no *, ?, [, {)Pruned by exact match against workspace-relative paths. ./-prefixed forms normalize to bare paths (./vendorvendor).
Glob pattern (contains metacharacters)Matched against workspace-relative paths using glob semantics.

.git is always ignored — you do not need to list it.

fs.skip vs per-plugin skip

The buildfile and go plugins each accept their own skip option (under options.skip on their respective plugins: entry). Those apply only to the respective plugin.

fs.skip is global: the engine merges it with every plugin's own skip list before building the final ignore set, so the same path is pruned consistently across every plugin.

Use fs.skip for workspace-wide exclusions (e.g. vendor, node_modules, generated output trees). Use per-plugin skip for patterns that should only affect one provider.

memCache — in-memory cache

Bounds the cache heph keeps in memory for the current run.

.hephconfig
memCache:
perEntryBytes: 16384 # largest single entry kept in memory
capacityBytes: 67108864 # total budget; 0 disables the in-memory cache

cache — local cache storage

Controls how heph stores artifacts in the durable on-disk cache.

.hephconfig
cache:
spillThresholdBytes: 8388608 # optional; default is 8 MiB
KeyTypeDefaultDescription
spillThresholdBytesnumber8388608 (8 MiB)Artifacts strictly larger than this are stored as plain files under <homeDir>/cache/blobs/. Smaller artifacts and all manifests stay in the cache database.

Raising the threshold keeps more artifacts in the database; lowering it spills more to plain files. The default works well for most projects.

caches — remote shared caches

A map of named remote caches. Each entry is keyed by a name you choose and describes one backend:

.hephconfig
caches:
shared:
uri: s3://my-bucket/heph-cache
read: true # default
write: true # default
concurrency: 256 # default

Multiple caches are allowed. heph writes to all writable caches in parallel and reads from the fastest first. Latency is measured once per process (or loaded from a persisted file) and refreshed when the definitions change.

KeyTypeDefaultDescription
uristringBackend URI. The scheme selects the backend (see table below).
readbooltrueWhether to consult this cache on a local miss.
writebooltrueWhether to push artifacts here after a local write. Writes happen on a background task — the build does not wait on the network.
concurrencynumber256Maximum in-flight requests to this cache at once. This is a shared ceiling across every target's pull/push against this cache, not a per-target limit.
endpointstringunsetBase URL of an S3-compatible service to talk to instead of AWS (e.g. Cloudflare R2, MinIO). s3:// caches only. See Custom S3-compatible endpoints.
regionstringunsetRegion to sign S3 requests for. s3:// caches only; unset leaves the choice to the AWS_REGION environment variable, which in turn defaults to us-east-1.

Supported URI schemes:

SchemeBackendCredentials
s3://bucket/prefixAmazon S3 (and compatible)AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY, AWS_PROFILE, or instance role
gs://bucket/prefixGoogle Cloud StorageGOOGLE_APPLICATION_CREDENTIALS or ADC
az://container/prefixAzure Blob StorageAZURE_STORAGE_ACCOUNT_NAME + AZURE_STORAGE_ACCESS_KEY
https://host/prefixGeneric HTTP object store
file:///pathLocal filesystem

endpoint and region apply only to s3:// caches — setting either on any other scheme is a hard error at startup, naming the offending field and URI. When set, both override the corresponding environment variable (AWS_ENDPOINT_URL, AWS_REGION) for that cache.

Every scheme's credentials can also be scoped to heph instead of read from the ambient environment — per store kind (HEPH_S3_*, HEPH_GCS_*, HEPH_AZURE_*, HEPH_HTTP_*) or per named cache (HEPH_CACHE_<NAME>_*, derived from the cache's key above). See Scoping credentials to a cache for the full precedence rules.

Run heph tool cache measure-latency to force a fresh latency measurement and print per-cache round-trip times. See the Remote cache guide for a complete walkthrough.

fuse — sandbox overlay

Controls whether sandboxes are assembled with a FUSE overlay instead of copying inputs. It is opt-in.

warning

The FUSE overlay is in development and not yet fully functional. Leave it off for production builds.

.hephconfig
fuse:
enabled: auto # true | false | auto
ValueBehavior
falseOff. The default when the block or key is omitted.
trueForced on.
autoThe engine decides per target.

See Sandbox for what the overlay changes.

lock — execute-phase lock backend

Selects how heph serializes concurrent access during execution.

.hephconfig
lock:
backend: fs # fs (default) | mem

fs coordinates across processes via the filesystem; mem keeps the lock in-process (useful for ephemeral or single-process runs).

telemetry — usage reporting

heph collects anonymous product metrics to guide development. No target addresses, file paths, labels, or any user-identifying information is ever reported.

Telemetry is on by default (opt-out). To disable it, add to .hephconfig:

.hephconfig
telemetry:
enabled: false

You can also opt out via environment variable, which takes precedence over the config file:

export HEPH_DISABLE_TELEMETRY=1

The environment variable is useful in CI pipelines or scripts where you do not want to touch the config file.

KeyTypeDefaultDescription
enabledtrue | falsetrueSet to false to disable all reporting.

Profiles — layered config overlays

Set the HEPH_PROFILES environment variable to a comma-separated list of profile names to layer additional config files on top of .hephconfig:

HEPH_PROFILES=ci heph run //...

For each name in the list, heph loads <name>.hephconfig from the workspace root and merges it over the accumulated config in order. HEPH_PROFILES=a,b loads the base .hephconfig, then applies a.hephconfig, then b.hephconfig — each merged over the result of the previous step.

A profile file that is missing or has invalid YAML is a hard error.

Merge rules

Config sectionBehavior
Scalar fields (homeDir, lock, fuse, telemetry, …)Profile value wins when present; base value is kept when absent.
cachesDeep-merged by cache name. A profile can patch individual fields (e.g. flip write, or re-point endpoint) while inheriting the rest of the entry from the base.
pluginsMerged by identifier (builtin/path/url value). A matching entry is replaced in place; new ones are appended, preserving order.

Disabling remote cache writes locally

The base config enables a shared cache for everyone. A dev profile turns off writes so local machines read from the cache without polluting it:

.hephconfig
caches:
shared:
uri: s3://my-bucket/heph-cache
read: true
write: true
dev.hephconfig
caches:
shared:
write: false # inherit uri and read from the base
HEPH_PROFILES=dev heph run //...