Skip to content

Commands

update [--no-imports] [-f unifiedscript.u]

Update the workspace dk.u or self-contained unifiedscript.u; updates imports and workspace assets. --no-imports skips imports. (dk0 updates workspace asset checksums with dk0 update, and invalidates values with dk0 invalidate or dk0 -x EXPRESSION.)

quickstart GROUP NAME [--dir DIR] [--base-registry URL]
[--group-registry GROUP=URL] [--force]

Bootstrap a new workspace from the recipe NAME in group GROUP. GROUP is the organisation namespace (e.g. ocaml) and NAME is the recipe within that group (e.g. opam). Fetches the recipe from the registry at runtime, writes a dk.u with the appropriate imports, then runs dk0 update automatically.

  • --dir DIR writes the workspace in DIR instead of the current directory.
  • --base-registry URL overrides the global base URL for the recipe registry (default: https://diskuv.com/dk/quickstart). Recipes are fetched from <URL>/<GROUP>/<NAME>.quickstart.jsonc. The environment variable DK_QUICKSTART_BASE_REGISTRY is a fallback checked before the built-in default. Useful for mirroring all groups without a per-group override.
  • --group-registry GROUP=URL points one group at its own registry. Recipes for that group are fetched from <URL>/<NAME>.quickstart.jsonc (the group is not part of the path). The environment variable DK_QUICKSTART_<GROUP>_REGISTRY (e.g. DK_QUICKSTART_OCAML_REGISTRY) is the equivalent fallback. The flag can be repeated for multiple groups. A group override takes priority over --base-registry for that group.
  • --force overwrites an existing dk.u in the target directory.

The registry serves <NAME>.quickstart.jsonc recipe files in the dk-quickstart-recipe-1.0 JSON schema, published at https://diskuv.com/dk/schema/dk-quickstart-recipe-1.0.json. A registry index (index.jsonc, dk-quickstart-index-1.0) lists the available groups, and each group publishes a group index (<group>/index.jsonc, dk-quickstart-group-index-1.0) listing its recipes.

A group does not have to live on diskuv.com. Any organisation can host a group on its own registry:

  • Serve each recipe file at <your-url>/<name>.quickstart.jsonc (one level; no group prefix is needed since --group-registry already selects the group).
  • Publish a group index at <your-url>/index.jsonc conforming to dk-quickstart-group-index-1.0.json so the diskuv.com listing page can enumerate your recipes.
  • Users reach your group with --group-registry <group>=<your-url> or by setting DK_QUICKSTART_<GROUP>_REGISTRY=<your-url> once (e.g. in a shell profile); existing commands keep working after a move.

To take over a group that started on diskuv.com, host the recipes and the group index at your own URL, then the group's entry in the diskuv.com registry index.jsonc is updated once to advertise your registry_url. After that you add new recipes (e.g. dune, rocq) by updating your own index.jsonc; diskuv.com refreshes its listing page on its own schedule.

add [-f unifiedscript.u] github-l2 [HOST/]OWNER/REPO[@TAG]

Add the latest release - or the release with tag TAG - of the GitHub repository [HOST/]OWNER/REPO to the workspace. The release must have a distribution with a SLSA Level 2 attestation.

test [--diff file] [--actual file] [--actual-in-place] unifiedtest.u

Run the unified test in <unifiedtest>.u (a file) or <unifiedtest>.u/run.u (a directory). A unified test is a superset of https://bitheap.org/cram/: each $ command is a value shell command or a dialog/exec command, and each %% command is a Lua chunk. Any interactive option for dialog/exec errors and exits non-zero. If # Title is a package id, a cell is created with the package's library as NAME and the parent directory (file) or the directory itself (dir) as VALUE (see --cell). The .t cram-test extension is also allowed. A self-contained unified test may carry an inline ## workspace to import libraries; otherwise the workspace is per the Configuration section. All workspace declarations are evaluated before the test commands.

  • ${RUNTIME} is a temp directory unique to the unified test. ${CONFIG} is for files in the cram-test directory, set by dk0 test.
  • --diff -|file saves diffs to a file or stdout (repeatable).
  • --actual -|file saves actual output to a file or stdout (repeatable).
  • --actual-in-place edits the unified-test file in place.
  • Default, if none of the above, is to print the diff.
  • exit 3: there is one or more diff of actual vs expected.
get-object MODULE@VERSION -s SLOT [-f|-d <output> [-n <strip>]] [-m <member>]

Get the contents of SLOT for the object MODULE@VERSION.

run-object MODULE@VERSION -s SLOT (-c COMMAND [-n <strip>] | -m MEMBER)
[-x <glob>]... [-e <glob>]... [-- [args...]]

Get SLOT of the object MODULE@VERSION into an anonymous directory and run COMMAND relative to that directory, or get and run MEMBER.

merge-object MODULE@VERSION -s SLOT [-f|-d <output> [-n <strip>]] [-m <member>]

Build the object MODULE@VERSION and merge its files into the output path.

get-asset MODULE@VERSION -p ASSET [-f|-d <output> [-n <strip>]] [-m <member>]

Get the contents of ASSET in the bundle MODULE@VERSION.

run-asset MODULE@VERSION -p ASSET (-c COMMAND [-n <strip>] | -m MEMBER)
[-x <glob>]... [-e <glob>]... [-- [args...]]

Get ASSET in the bundle MODULE@VERSION into an anonymous directory and run COMMAND relative to that directory, or get and run MEMBER.

get-bundle MODULE@VERSION [-f|-d <output> [-n <strip>]]

Get all the bundle files in the bundle MODULE@VERSION.

run-function MODULE@VERSION [-f|-d <output> [-n <strip>]] [-m <member>]
[requestparam=value ...]

Run the function rule MODULE@VERSION and write the resulting object. Example:

dk0 run-function MyLibrary_Std.A.B.MyModule.MyRule@1.0.0 -s Some.Slot -- a=1 b=2
enter-object MODULE@VERSION -s SLOT

Launch a shell with the environment and file content of SLOT for object MODULE@VERSION. The shell may be skipped if MODULE@VERSION -s SLOT is up-to-date. The shell is $SHELL (if unset /bin/sh) on Unix, and pwsh.exe, powershell.exe or cmd.exe on Windows. SHELL_SLOT is set to the -s SLOT output directory; SHELL_SLOTS to the output parent directory of all slots.

dialog [-e stat] [-l [g=]modname[@version]] [-i] MODULE@VERSION [name1=val1 ...]

Run the interactive (UI) rule MODULE@VERSION. The JSON request is created from the name1=val1 ... name-values per https://www.w3.org/TR/html-json-forms/ (e.g. pet[name]=Dot kids[1]=Zoe{"pet":{"name":"Dot"},"kids":[null,"Zoe"]}). -e stat evaluates the Lua statement stat; -l modname is equivalent to require(modname) (or g = require(modname).at(version) for -l g=modname@version). -e, -l and the UI rule are handled in the order they appear; -i enters interactive mode at the end. (dialog is the reference implementation's subcommand for UI rules, while run-function is reserved for function rules.)

exec [-e stat] [-l [g=]modname[@version]] [-i] [--lua] script [args...]

Run script. An embedded interactive rule is searched first; if none is found and either --lua is given or script ends with *.lua[u], the Lua script is run. args is available to the script as strings in a global table arg per https://www.lua.org/manual/5.4/manual.html#7. -e, -l and script are handled in order; -i enters interactive mode at the end.

lua [--analysis] [--valuescan] [-e stat] [-l [g=]modname[@version]] [-i]
[script [args...]]

Launch a Lua REPL. Each REPL line is evaluated as a Lua chunk and any return values are printed. --analysis enters the analysis mode used during the VALUESCAN phase; dk0 lua --analysis somefile.lua shows the rules the build system thinks are defined in a Lua script. --valuescan adds a VALUESCAN phase where -e, -l and script are scanned and any require() are resolved before evaluation. If script is given it is executed as a Lua script; -i enters interactive mode afterward (default when no script and stdin is a terminal). dk0 also runs a file directly as a Lua script - dk0 run some-script.lua - when the file ends with .lua (and some other extensions); see dk0 --help.

dk0 lua -la b.lua t1 t2
dk0 lua -e "print(arg[1])"
remote UI_MODULE@VERSION [REMOTE_OPTION=VALUE...] [REPOSITORY] COMMAND...

Run a local value shell command, test, lua, dialog, exec, or deprecated run command on a remote execution engine. Short forms expand the library id - e.g. GitHub@0.1.0CommonsBase_Remote.GitHub@0.1.0, BuildBuddy.Cloud@0.1.0BuildBuddy_Remote.Cloud@0.1.0.

signify -G [-c comment] -p pubkey -s seckey
signify -S [-x sigfile] -s seckey -m message
signify -V [-q] [-p pubkey] [-x sigfile] -m message

Create and verify cryptographic signatures: -G generates a key pair, -S signs a message, -V verifies a message against a signature.

zip ZIPFILE[.zip] [SRCFILE...]

Create or update a zipfile. Without SRCFILE, all files under the source directory are added. -srcdir DIR sets the source directory; -d deletes entries; -x PATTERN excludes a glob; --deterministic creates a reproducible zipfile.

dk0 requires that the package (a common prefix for a set of modules) is a VendorQualifier_Unit library id. For example, CommonsBase_GNU.Make.Apparatus is not an acceptable package for distribution but CommonsBase_GNU is.

prepare-version [--ci github] [--prepare-dir DIR] MAJOR.MINOR

Create key pairs for MAJOR.MINOR, the next minor, the next even minor, and the next major version, and add public keys to etc/dk/d/MAJOR.MINOR.PATCH.dist.json. Existing public keys are skipped. Asks for and sets the license if not present. Fails if keys exist but MAJOR.MINOR is not the next minor or major version.

  • --ci github creates the GitHub workflow .github/workflows/distribute-MAJOR.MINOR.yml and prints the GitHub CLI command to register the MAJOR.MINOR secret key (default).
  • --prepare-dir DIR places keys in DIR (default etc/dk/d).
distribute --library LIBRARY@VERSION [--with-producer-version VERSION]
[--no-valuestore] [--diff FILE] [--actual FILE] [--prepare-dir DIR]
PART unifiedtest.t

Evaluate the commands in unifiedtest.t, then define a distributed bundle and a distribution LIBRARY@VERSION in dk-dist/PART.values.json. PART avoids conflicts when combining many bundles with combine. Set up LIBRARY@VERSION first with prepare-version ... MAJOR.MINOR; VERSION must be a monotonically increasing patch of MAJOR.MINOR without build or prerelease components. The LIBRARY is added to --trust-local-package. The distributed bundle is relative to dk-dist/ with ./PART.distmeta.valuestore.zip + ./PART.package.valuestore.zip (metadata and package payload valuestores) and ./PART.distmeta.TAG.tracestore + ./PART.package.TAG.tracestore (tracestores; TAG is the compatibility tag, e.g. oc414_wd64 - see --print-platform-ids). Overwrites distributions[].producer.application with the current dk0 version or --with-producer-version.

  • --no-valuestore disables creation of valuestore.zip.
  • --diff/--actual/--actual-in-place behave as in test.
  • --prepare-dir DIR finds keys in DIR (default etc/dk/d).
  • exit 3: there is one or more diff of actual vs expected.

Objects and rules are public interfaces, so only their transitive values are included; bundle files and assets are included only if an object or rule transitively has get-asset/get-bundle precommands.

combine [--origin-name <origin-name>] [--mirror <origin-url>]*

Combine dk-dist/*.values.json from many distribute PART runs into a single dk-dist/values.json containing one or more distributions. Any --mirror origin-url become the bundle mirrors for the origin named local, which is then renamed to origin-name. (dk0 combine can also adjust the mirrors permanently during distribution.)

import github-l2 -R,--repo [HOST/]OWNER/REPO [--tag TAG] [--outdir DIR]
import local --path VALUES.JSON [--outdir DIR]

import github-l2 verifies the distributions in values.json and related assets (valuestores and tracestores) from the GitHub release with tag TAG (or latest), using GitHub's SLSA Level 2 attestation. If verified, the values.json with the attestation embedded is saved to DIR/LIBRARY.values.json (DIR default <workspace>/etc/dk/i). Values and traces are not imported unless DIR is on the include path and the distribution libraries are referenced. import local saves a distribution from VALUES.JSON to DIR and discovers transitive distributions from its tracestores and valuestores - for local/offline workflows and tests.

restore github-l2 [HOST/]OWNER/REPO[@TAG] [--tag-before TAG]

Reuse a previous GitHub release as a lazy build cache instead of a size-constrained, all-or-nothing CI cache (e.g. GitHub Actions cache). Verifies the latest release (or the release with tag TAG) with GitHub's SLSA Level 2 attestation, saves its values.json to <workspace>/etc/dk/i, then seeds the trace store (t/c) and lazy value pointers (t/d) by forcing the release's distributions with --import lazy. Only the small *.valuestore.index files are downloaded eagerly; individual value blobs are range-fetched on demand by the next distribute.

If no matching release exists, no-op (exit 0).

If matching release can't be read, erases valuestore and tracestore to maintain future incrementality.

HOWTO: Change the producer (attestation) repository

Section titled “HOWTO: Change the producer (attestation) repository”

Background: The GitHub repository that prepare-version --ci github prints in its gh api .../environments/dk-distribution and gh secret set --repo ... commands comes from producer.github_slsa_v1_l2.repository (or the SLSA L3 caller repository if set) in your latest existing etc/dk/d/*.dist.json. This same field is the SLSA attestation subject a consumer verifies at import-github-l2.

To point a new version's keys, secrets, and attestation at a different repository, the safe sequence is:

  1. Temporarily edit "repository" in the latest existing version's etc/dk/d/<prev>.dist.json to the new repository.
  2. Run prepare-version --ci github <new MAJOR.MINOR>. The generated <new>.dist.json, workflow, and printed gh commands now target the new repository.
  3. Revert the "repository" change in the old <prev>.dist.json so it still reflects how that already published version was attested.

When the consumer uses dk0 it will verify the SLSA attestation against the previous repository, so consumers must be told to update their import expressions in dk.u to the new repository for the new version.

--tag-before TAG selects the most recent release before TAG with the same MAJOR.MINOR. The intended usage is for a GitHub workflow to set TAG to the git tag it is currently building, and restore gets its cache from the release that immediately preceded it.

--tag-before and @TAG are mutually exclusive.

If the GitHub workflow listing is unavailable from dk0's embedded GitHub CLI (ie. no authentication, offline, or a gh error), it is treated as "no candidate release" and restore degrades to a cold build.

A restore tag is MAJOR.MINOR.TIMESTAMP, where TIMESTAMP is one of these UTC stamp formats:

DigitsFormExample
8YYYYMMDD20260701
10YYYYMMDDHH2026070100
12YYYYMMDDHHMM202607010017
14YYYYMMDDHHMMSS20260701001730

Resolution algorithm:

  1. Split TAG into MAJOR.MINOR and TIMESTAMP.
  2. List the repository's release tags with the embedded gh, keeping only tags of the form MAJOR.MINOR.TIMESTAMP with the same MAJOR.MINOR line.
  3. Compare timestamps by zero-padding each to 14 digits.
  4. Keep the candidates whose padded timestamp is strictly less than TAG's, and select the greatest. If none remain, that indicates a cold build (exit 0).
invalidate [origin:ORIGIN:subpath:PATH]... [FILE.values.{jsonc,lua}]...

Invalidate matching traces, including other traces that depend on them. origin:... invalidates assets obtained through get-asset whose origin is ORIGIN and whose path is PATH or a descendant (empty PATH is all paths); assets obtained through get-bundle are not invalidated. FILE.values.jsonc / FILE.values.lua invalidate values in FILE.

query

(Unstable; options will change.) List all build traces in the trace store. Exit 2 when there is an error reading the trace store.

query manifest [-f WORKSPACE.u] [--markdown] [--outfile FILE] [--version VERSION]

Read a dk package's local working tree (dk.u workspace script and dist/*.u or dist-*.u/run.u distribution scripts) and emit a dk.package-manifest/2 JSON document to stdout (or --outfile FILE).

FlagMeaning
-f WORKSPACE.uExplicit workspace file path. Defaults to nearest ancestor dk.u with a ## Workspace section.
--markdownEmit Markdown (overview + body) instead of JSON.
--outfile FILEWrite output to FILE instead of stdout.
--version VERSIONSet the package.version field. Required unless the package provides one.

The schema field identifies the manifest format and its version, currently dk.package-manifest/3 (version 3). The version number increases whenever the JSON structure changes in a way that could break a reader; a tool can check it before reading. The top-level fields are schema, package, licenses, platforms, assets, modules, dependencies, overview, and body. Module kinds are inferred from the distribution scripts: apparatus (workspace assets via % unified.asset), bundle (title contains .Bundle@), scriptmodule (has run or post-object commands), and object (has \dk.object metadata or get-object commands in the expected output).

Each module has a builds list (one entry per \dk.object, with platform, abi and valueId) and, when the module runs get-asset, an assets list (one entry per \dk.asset, with path, valueId and byteSize). Object builds have no size. Object payload bytes are not deterministic across builds, which rules out size as a stable identifier. Asset value blobs are content addressed and keep a deterministic byteSize.

Version 3 changed the format from version 2 by removing byteSize from each build and adding the per-module assets list. Manifests produced before this release use version 2 and still carry build sizes.