Type Reference
When you declare an option with Pointy's types, you get Nix validation and browser form rendering from a single definition, so you never describe the same field twice. Declarations go inside options.pointy.<name>, usually under with config._pointy.lib.types; in the template's options section.
Pointy serializes those declarations into .#pointy.stepConfig. The frontend reads that output and generates validation rules, input widgets, display labels, and field constraints from it. Because the same module that defines the types later evaluates the build, the form stays aligned with what the build will receive: change the Nix type, and validation, widgets, labels, constraints, and build evaluation update together.
Top-level template metadata
These fields sit at the top level of a template file, alongside sortKey, pointy.type, and module:
displayName(optional): short label shown in table headings, modal titles, and the type chip in step selectors. When omitted, the raw attribute name is used.description(optional): intro paragraph rendered in the create/edit modal.sortKey(optional): integer that orders step-type sections in the project view.
Example:
{
sortKey = 3;
displayName = "Alignment";
description = "Align reads to a library with STAR.";
pointy.type.derivation = { };
module = { ... };
}For per-option labels and hints, see pointy.string / pointy.step below.
Step type declarations
Step type declarations live at the top level of a template file, outside module, because they describe how Pointy handles the step as a whole rather than one argument within it.
pointy.type.derivation
pointy.type.derivation = {
withSrcFiles = false; # default
};This declares a runnable derivation step.
withSrcFiles = truemakes Pointy symlink every top-level entry fromsrcFiles/<step-id>/into the build working directory before the build runs, and the frontend shows a Source Files section for that step type.
pointy.type.fileUpload
pointy.type.fileUpload = {
allowedExtensions = [ ".fastq.gz" ".fastq" ];
};This declares a file upload step.
allowedExtensionsrestricts the frontend file picker.- file-upload templates usually also declare an
uploadedoption of typelib.types.package; Pointy fills that option automatically.
At build time, cfg.uploaded is a Nix store path pointing at the uploaded payload directory.
pointy.type.download
pointy.type.download = { };This declares a download step. The declaration takes no attributes: it marks the template as a download kind, which causes the backend to prefetch the user-supplied URL and store a trusted hash.
The frontend generates a single URL text field from this declaration. Template authors do not define url as a module option; the field is injected automatically. The URL must be an HTTP or HTTPS URL with a nonempty host and no whitespace.
At build time, the standard library resolves the downloaded option via pkgs.fetchurl:
pkgs.fetchurl { inherit (value) url hash; }where value is the stored args.downloaded = { url; hash; } produced by the backend at save time. Since the hash is captured during step creation or URL edit, the build is purely a fixed-output fetch: the network is never contacted during evaluation.
A download template's module must declare a downloaded option:
options.pointy.<name>.downloaded = lib.mkOption {
type = lib.types.package;
};The resolved cfg.downloaded is a Nix store path pointing at the fetched file and can be used directly in installPhase, symlinked into $out, or processed further like any other derivation input.
Uploaded vs. downloaded: file-upload steps receive user-supplied binary payloads through the browser; download steps fetch a single remote file once and store its hash for reproducibility. Both resolve to a store path (cfg.uploaded / cfg.downloaded) the template can consume identically.
Option types
Option types are used inside options.pointy.<name> to describe the arguments users fill in through the generated form. Their Nix-level value remains the authority: display metadata may alter labels or widgets, but it does not create a parallel frontend-only field definition.
pointy.string
lib.mkOption {
type = pointy.string {
displayName = "Extra STAR args"; # optional, used as form label
description = "Help text shown under the field.";
display = { ... }; # optional, see below
};
default = ""; # optional
}By default this renders a plain text input. The optional display attribute changes the widget:
display value | Widget rendered |
|---|---|
| (omitted) | Plain single-line text input |
display.command = "tool-name" | Command-prefixed argument box |
display.textarea = { } | Auto-growing multi-line textarea |
display.code.language = "python" | Syntax-highlighted code editor |
A string type can also expose an autocomplete key. The current UI uses that key for autocomplete-backed string lists inside pointy.record fields:
packages = lib.mkOption {
type = pointy.listOf (pointy.string {
displayName = "nixpkgs packages";
description = "Packages to install before running the script.";
autocomplete = "packages";
});
default = [];
};The frontend sends the key in autocomplete to .#pointy.autocomplete.<template>.<key>. The autocomplete function receives an attribute set containing query, limit, and any simple string or enum values from the surrounding record as context. It must return a JSON-serializable list of strings. Users can still enter free-text values when the suggestion list does not contain what they need.
pointy.enum
lib.mkOption {
type = pointy.enum {
values = [ "singleEnd" "pairedEnd" ];
displayName = "Read layout";
description = "How the reads are organized.";
valueDisplayNames = {
singleEnd = "Single-end";
pairedEnd = "Paired-end";
};
};
default = "pairedEnd";
}This renders a dropdown. The stored value is the raw string from values; valueDisplayNames only changes the label shown in the UI.
pointy.step
lib.mkOption {
type = pointy.step {
displayName = "Reads"; # optional, used as form label
description = "Help text shown under the field.";
allowedTypes = [ "typeA" "typeB" ]; # optional
};
}This renders a step selector filtered by allowedTypes. If allowedTypes is omitted, any step type is allowed.
The selector is scoped to steps already assigned to the current project, so the project acts as the visible boundary of the workflow even though steps can be shared.
To reference a step from another project, first attach it via Add from other project. Existing saved references that fall outside the current project are still shown, labeled not in project, so they remain visible and can be repaired deliberately rather than silently discarded.
At build time, the selected value resolves to the Nix store path of the upstream output and can be used directly in installPhase.
See Building Workflows (Steps).
pointy.listOf
lib.mkOption {
type = pointy.listOf (pointy.step {
displayName = "Step deps";
description = "Other steps to symlink into the build directory.";
});
default = [];
}
# or a list of strings:
lib.mkOption {
type = pointy.listOf (pointy.string {
displayName = "nixpkgs deps";
description = "Attribute paths into pkgs.";
});
default = [];
}This wraps another Pointy type to make it repeatable. The UI renders an add/remove list, and the final value becomes a Nix list of the resolved inner values. The inner type's displayName and description apply to the whole list field.
pointy.record
lib.mkOption {
type = pointy.listOf (pointy.record {
displayName = "Samples";
fields = {
sample = pointy.string {
displayName = "Sample";
description = "Sample identifier.";
};
condition = pointy.enum {
values = [ "control" "treated" ];
description = "Experimental condition.";
};
packages = pointy.listOf (pointy.string {
description = "Packages used for this sample.";
autocomplete = "packages";
});
};
});
default = [];
}A record renders as a repeatable group of related fields and is appropriate when one logical argument cannot be represented coherently as parallel, unrelated lists. Autocomplete requests from fields inside the record include its simple string and enum values as context, allowing suggestions to respond to the row being edited while the final value remains a normal structured Nix value.
Hidden options
Fields marked visible = false are omitted from the generated step schema and therefore from the form, while remaining available to module evaluation. Use this for values supplied by Pointy or by the template itself, not merely to conceal a user-configurable argument.
The id option
Every template should declare an id option as follows:
id = lib.mkOption {
type = pointy.string { description = ""; };
visible = false;
};Pointy populates id automatically with the numeric step identifier.
Templates normally forward it into passthru.meta.pointy:
passthru.meta.pointy = {
type = "<template-name>";
inherit (cfg) id;
};This keeps the step id available to templates and to downstream tooling such as scripts that inspect meta.pointy.
