Commands
High-level commands
Section titled “High-level 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 DIRwrites the workspace inDIRinstead of the current directory.--base-registry URLoverrides 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 variableDK_QUICKSTART_BASE_REGISTRYis a fallback checked before the built-in default. Useful for mirroring all groups without a per-group override.--group-registry GROUP=URLpoints 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 variableDK_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-registryfor that group.--forceoverwrites an existingdk.uin 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.
Hosting a group on your own registry
Section titled “Hosting a group on your own registry”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-registryalready selects the group). - Publish a group index at
<your-url>/index.jsoncconforming todk-quickstart-group-index-1.0.jsonso the diskuv.com listing page can enumerate your recipes. - Users reach your group with
--group-registry <group>=<your-url>or by settingDK_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.uRun 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 bydk0 test.--diff -|filesaves diffs to a file or stdout (repeatable).--actual -|filesaves actual output to a file or stdout (repeatable).--actual-in-placeedits 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.
Value shell commands
Section titled “Value shell commands”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=2enter-object MODULE@VERSION -s SLOTLaunch 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.
Script commands
Section titled “Script commands”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 t2dk0 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.0 ⇒ CommonsBase_Remote.GitHub@0.1.0, BuildBuddy.Cloud@0.1.0
⇒ BuildBuddy_Remote.Cloud@0.1.0.
Utility commands
Section titled “Utility commands”signify -G [-c comment] -p pubkey -s seckeysignify -S [-x sigfile] -s seckey -m messagesignify -V [-q] [-p pubkey] [-x sigfile] -m messageCreate 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.
Distribution commands
Section titled “Distribution commands”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.MINORCreate 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 githubcreates the GitHub workflow.github/workflows/distribute-MAJOR.MINOR.ymland prints the GitHub CLI command to register theMAJOR.MINORsecret key (default).--prepare-dir DIRplaces keys inDIR(defaultetc/dk/d).
distribute --library LIBRARY@VERSION [--with-producer-version VERSION] [--no-valuestore] [--diff FILE] [--actual FILE] [--prepare-dir DIR] PART unifiedtest.tEvaluate 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-valuestoredisables creation ofvaluestore.zip.--diff/--actual/--actual-in-placebehave as intest.--prepare-dir DIRfinds keys inDIR(defaultetc/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:
- Temporarily edit
"repository"in the latest existing version'setc/dk/d/<prev>.dist.jsonto the new repository. - Run
prepare-version --ci github <new MAJOR.MINOR>. The generated<new>.dist.json, workflow, and printedghcommands now target the new repository. - Revert the
"repository"change in the old<prev>.dist.jsonso 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.
Selecting the release with --tag-before
Section titled “Selecting the release with --tag-before”--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:
| Digits | Form | Example |
|---|---|---|
| 8 | YYYYMMDD | 20260701 |
| 10 | YYYYMMDDHH | 2026070100 |
| 12 | YYYYMMDDHHMM | 202607010017 |
| 14 | YYYYMMDDHHMMSS | 20260701001730 |
Resolution algorithm:
- Split
TAGintoMAJOR.MINORandTIMESTAMP. - List the repository's release tags with the embedded
gh, keeping only tags of the formMAJOR.MINOR.TIMESTAMPwith the sameMAJOR.MINORline. - Compare timestamps by zero-padding each to 14 digits.
- 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).
Query commands
Section titled “Query commands”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).
| Flag | Meaning |
|---|---|
-f WORKSPACE.u | Explicit workspace file path. Defaults to nearest ancestor dk.u with a ## Workspace section. |
--markdown | Emit Markdown (overview + body) instead of JSON. |
--outfile FILE | Write output to FILE instead of stdout. |
--version VERSION | Set 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.