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.
version: 1.2.3
plugins:
- builtin: buildfile
- builtin: bash
Pinning the version
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 channel — dev 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:
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.
| Key | Type | Default | Description |
|---|---|---|---|
version | string | unset | Pins 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. |
versionFlavour | string | "" (std) | Selects which release flavour self-upgrade downloads: "" for std, or debug for the unstripped build. See Pinning a release flavour. |
plugins | list 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. |
homeDir | path | unset | Where heph keeps its home and cache. |
fs | {skip: string[]} | {} | Workspace-wide ignore patterns shared with all tree-walking plugins. |
memCache | {perEntryBytes, capacityBytes} | unset | In-memory cache sizing. |
cache | {spillThresholdBytes: number} | {} | Durable local-cache tuning. |
caches | map of {uri, read, write, concurrency, endpoint, region} | {} | Named remote (shared) caches. |
fuse | {enabled: true | false | "auto"} | off | Sandbox overlay mode. |
lock | {backend: fs | mem} | fs | Execute-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:
| Key | Loads |
|---|---|
builtin | A compiled-in plugin by name (e.g. buildfile, exec). |
path | A local *-plugin.json manifest (relative to the workspace root, or absolute). |
url | A remote *-plugin.json manifest — downloaded and cached in ~/.heph/plugins/. |
Each entry also accepts an optional options map passed to that plugin:
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.
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.
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.
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.
| Format | Meaning |
|---|---|
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.
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 shape | Effect |
|---|---|
Literal path (no *, ?, [, {) | Pruned by exact match against workspace-relative paths. ./-prefixed forms normalize to bare paths (./vendor → vendor). |
| 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.
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.
cache:
spillThresholdBytes: 8388608 # optional; default is 8 MiB
| Key | Type | Default | Description |
|---|---|---|---|
spillThresholdBytes | number | 8388608 (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:
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.
| Key | Type | Default | Description |
|---|---|---|---|
uri | string | — | Backend URI. The scheme selects the backend (see table below). |
read | bool | true | Whether to consult this cache on a local miss. |
write | bool | true | Whether to push artifacts here after a local write. Writes happen on a background task — the build does not wait on the network. |
concurrency | number | 256 | Maximum 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. |
endpoint | string | unset | Base 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. |
region | string | unset | Region 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:
| Scheme | Backend | Credentials |
|---|---|---|
s3://bucket/prefix | Amazon S3 (and compatible) | AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY, AWS_PROFILE, or instance role |
gs://bucket/prefix | Google Cloud Storage | GOOGLE_APPLICATION_CREDENTIALS or ADC |
az://container/prefix | Azure Blob Storage | AZURE_STORAGE_ACCOUNT_NAME + AZURE_STORAGE_ACCESS_KEY |
https://host/prefix | Generic HTTP object store | — |
file:///path | Local 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.
The FUSE overlay is in development and not yet fully functional. Leave it off for production builds.
fuse:
enabled: auto # true | false | auto
| Value | Behavior |
|---|---|
false | Off. The default when the block or key is omitted. |
true | Forced on. |
auto | The engine decides per target. |
See Sandbox for what the overlay changes.
lock — execute-phase lock backend
Selects how heph serializes concurrent access during execution.
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:
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.
| Key | Type | Default | Description |
|---|---|---|---|
enabled | true | false | true | Set 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 section | Behavior |
|---|---|
Scalar fields (homeDir, lock, fuse, telemetry, …) | Profile value wins when present; base value is kept when absent. |
caches | Deep-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. |
plugins | Merged 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:
caches:
shared:
uri: s3://my-bucket/heph-cache
read: true
write: true
caches:
shared:
write: false # inherit uri and read from the base
HEPH_PROFILES=dev heph run //...