Skip to content

daft env

Print deterministic per-worktree env values (ports, names)

Description

Print deterministic per-worktree env values derived from the worktree's name.

Every value is a pure function of (salt, worktree slug, declaration) — no allocation, no registry, no daemon. The same inputs give the same answer on every machine, from every directory, even for a worktree that does not exist yet, so services in different worktrees (and different repos) can find each other with zero coordination: compute, don't communicate.

Ports hash the worktree to a contiguous block (default 16 ports in 20000-32767) and declared names take small offsets inside it, so one worktree's ports are consecutive and collisions between worktrees require whole-block hash collisions, which daft warns about. Undeclared names resolve in a disjoint ad-hoc region when the repo declares no env: schema; declaring one makes unknown names an error (--ad-hoc overrides).

The positional address is [repo:]VAR[@worktree] — 'daft env API_PORT', 'daft env backend:API_PORT' (that repo's worktree matching this one's name), 'daft env API_PORT@feat-x'. --repo/--worktree are the canonical spellings of the same qualifiers. Unlike other commands' bare --repo (which targets the default-branch worktree), env's cross-repo default is the worktree matching the current one's name: an address names a value's coordinate, not an execution target, and the matching-name convention is how one feature spans repos.

Derivation knobs (--salt, --range, --block-size, --offset) answer "what would it be": overriding them computes a hypothetical — useful to preview a salt change before committing it — and daft notes on stderr when the answer differs from the configured one.

Daft's own job variables are answered too, for debugging what a hook or task will see without running it. The listing shows the at-rest DAFT_* set — the seven a task receives whose values the worktree alone determines (DAFT_PROJECT_ROOT, DAFT_GIT_DIR, DAFT_REMOTE, DAFT_SOURCE_WORKTREE, DAFT_WORKTREE_PATH, DAFT_BRANCH_NAME, DAFT_IS_NEW_BRANCH) — and naming one prints its value. They are built from the same context a task runs under rather than recomputed, so the answer cannot drift from the job. Two carry a caveat for lifecycle hooks specifically: a hook's DAFT_REMOTE is its operation's remote (daft.remote, when set) and a create or remove hook's DAFT_SOURCE_WORKTREE is wherever the command was invoked, not the worktree named here.

Every other DAFT_* variable is event-scoped — DAFT_HOOK, DAFT_COMMIT, DAFT_REMOVAL_REASON, DAFT_MERGE_*, ... — and exists only while its operation runs; asking for one explains when it is set instead of guessing a value, and an unrecognized name is reported as such rather than answered. None of them are derived, so derivation flags do not apply, and serializing surfaces (--export, --write, --format) stay the declared set: they feed files and machines that keep what they read, and these values go stale the moment the worktree moves.

Declare values under env: in daft.yml (see the yaml reference). Injection: hooks, tasks, and daft exec receive declared values automatically; shells via eval "$(daft env --export)" (direnv/mise); file-reading tools via daft env --write (dotenv). Injected values never override variables already set in the parent environment.

Usage

daft env [OPTIONS] [VAR]

Arguments

ArgumentDescriptionRequired
<VAR>Value address: VAR, repo:VAR, VAR@worktree, or repo:VAR@worktree. Omit to list every declared value for the addressed worktreeNo

Options

OptionDescriptionDefault
--repo <NAME>Read another cataloged repository's values (canonical form of the repo: address prefix)
--worktree <NAME>Address a specific worktree by name (canonical form of the @worktree address suffix). Works for worktrees that do not exist yet — the value is already determined
--salt <SALT>Override the hash salt (default: env.salt, else the repo directory name). Answers "what would my values be under this salt"
--range <START-END>Override the port range (default: env.range, else 20000-32767)
--block-size <N>Override the ports-per-worktree block size (default: env.block_size, else 16)
--offset <N>With VAR: compute the port at this offset in the worktree's block, regardless of what the schema declares
--exportEmit export NAME='value' lines for the shell (eval "$(daft env --export)")
--write <PATH>Write the declared values as a dotenv file. PATH defaults to env.write from daft.yml; - writes the dotenv text to stdout
--ad-hocResolve an undeclared name to an ad-hoc port even though this repo declares an env: schema
--format <FORMAT>Output format. Mutually exclusive with --template
--template <STR>Tera template string. Mutually exclusive with --format
--no-headersOmit header row (tsv/csv only)

Global Options

OptionDescription
-h, --helpPrint help information
-V, --versionPrint version information

Released under MIT or Apache-2.0.