For Instance Admins

Type Reference

Settings for templates and forms, for administrators adding analysis tools.

This reference is for administrators writing templates. A template should not have to describe the same input twice: the language declaration sets the argument names, types, order, and defaults; the Nix template chooses form controls and runs the program. For a complete example, start with Setting Up the User Repository.

This reference describes the main branch of pointy-stdlib and step-config version 4, which the current frontend requires. It does not describe the older compile.options API. Keep the stdlib, language, and language-library revisions together; the setup guide explains the current input prerequisites.

Top-level template metadata

A template is an attribute set loaded from templates/<name>.nix. Its filename is the key used by step definitions, preset template lists, and folder template lists.

AttributeMeaning
displayNameHuman-readable template label. Required; null lets the UI use the template key.
descriptionExplanation shown with the template. Required; a string or null.
sortKeySection ordering. Required; an integer or null.
iconOptional icon name for the template's controls.
contract.interfaceRequired interface name declared in the language entry program.
bindingsPresentation overrides keyed by language parameter name; defaults to { }.
pointy.typeExactly one of the step kinds below.
compileFunction receiving { lib, pkgs, pointyLib } and returning build.
requirementsOptional resource attribute set or function of resolved args.
extrasOptional "csv" or "fastq" metadata scan.
autocompleteOptional function receiving { pkgs, lib } and returning named suggestion functions.

The renderer reads displayName, description, and sortKey directly, so all three attributes must exist even when their value is null.

Unlike the old typed-template API, these templates are plain attribute sets.

Step type declarations

pointy.type.derivation

pointy.type.derivation = { withSrcFiles = true; };

This is the Nix build behind an analysis step. withSrcFiles defaults to false; when enabled, the stdlib links srcFiles/<step-id>/ entries into the build working directory and the UI shows them beside the step.

pointy.type.fileUpload

pointy.type.fileUpload = {
  allowedExtensions = [ ".fastq.gz" ".fastq" ];
};

The file picker uses allowedExtensions; supply null to leave the selection unrestricted. This attribute is required. It is a picker hint, not content validation. Pointy fills args.uploaded from the uploaded payload hash and resolves it to a store object for the builder. Do not add an uploaded language parameter or binding. Upload templates normally have a parameterless interface describing the output.

pointy.type.download

pointy.type.download = { };

Declare a url : String language parameter and optionally give bindings.url a label or help text. Saving the step prefetches the URL and records a trusted args.downloaded object with url, hash, and downloadedAt. The builder receives args.downloaded resolved through pkgs.fetchurl, not the raw object.

The renderer appends a read-only Downloaded at field using path = [ "downloaded" "downloadedAt" ];. Do not add a binding for that synthetic field. The URL must be HTTP or HTTPS; see Download steps for the save and reuse behavior.

Contracts and argument values

For example, an entry program can contain:

program example {
  type Layout = "single" | "paired"

  struct Sample
    (name : String)
    (layout : Layout)
    (packages : List String)
    : Sample

  struct Analysis
    (body : String = "")
    (threads : Int = 2)
    (keepIntermediate : Bool = False)
    (reads : @fulfill Directory)
    (dependencies : List (@fulfill Directory))
    (samples : List Sample = [])
    : Directory
}

Finite choices are declared with type, as in type Layout = "single" | "paired". data does not declare a choice: a data name used as a parameter type leaves the form unrenderable.

contract.interface = "Analysis"; selects these parameters. Data arguments become ordinary Nix strings, integers, booleans, lists, and records. An input from another step, such as reads, is stored as { step = 42; } and resolved to that step's derivation for the builder. Multiple step inputs are stored as a list of those references; an omitted list of step inputs resolves to an empty list.

The language declaration determines defaults and required arguments. The stdlib rejects unknown names and missing required values with pointy.<type>: unknown arg(s): ... or pointy.<type>: missing required arg(s): ..., then passes resolved arguments to build. Evaluating the form document builds nothing; when a step runs, Pointy builds its certificate against the declared output type. A step record may also carry a top-level refine = "<type expression>"; it is part of the declared output type for certificates and never reaches the build derivation.

Bindings

A binding changes presentation, not the declaration. Its name must match a language parameter. A record override must name a real record field. Unknown binding names, record-field overrides, and unsupported widget choices fail configuration evaluation instead of silently adding fields.

For the Analysis interface above:

bindings = {
  body = {
    displayName = "Script";
    description = "Shell code to run for this analysis.";
    widget = { kind = "code"; language = "shell"; };
  };
  threads.displayName = "Threads";
  reads = {
    displayName = "Input reads";
    quickCreate = true;
  };
  dependencies.list.displayName = "Extra reads";
  samples.list.record.fields = {
    name.displayName = "Sample name";
    layout.enumDisplayNames = {
      single = "Single-end";
      paired = "Paired-end";
    };
    packages.widget = { kind = "tokens"; hook = "packages"; };
  };
};

Here packages is an autocomplete hook you provide. An outer list's labels belong on the outer binding. Element overrides go under list; record-field overrides go under record.fields.<name>. Nest those once for each layer of the declared shape.

ShapeBinding keys
Text, integer, or booleandisplayName, description, widget, autocomplete, readOnly, path
ChoicedisplayName, description, widget, enumDisplayNames
Step input (subject)displayName, description, widget, quickCreate
List (array)displayName, description, widget, list
RecorddisplayName, description, record

enumDisplayNames changes labels, not stored values.

A step-input picker is driven by the language, not by a binding key. The form offers every loaded step, linked or unfiled, whose template's output meets the parameter's declared type (the field's accepts); the type system's outright proofs (proven) decide which rows get the quick-create shortcut. There is no allowedTypes key. A saved reference whose step is not loaded shows as #<id> (missing).

quickCreate = true on a step-input binding adds a Create … row action to steps whose template the field proves meets the parameter's type; the label names the target template. The action opens the target step's form with this input already selected. It appears only for target templates that steps in the same folder already use. For a list of step inputs, put it on the element: bindings.dependencies.list.quickCreate = true;.

Widgets

A widget is a form control. Pointy chooses a default to match the argument's type; declare widget only to change the control or add an autocomplete hook.

Argument shapeDefaultOther supported presentation
Texttexttextarea, code, command, datetime
IntegernumberUse number with this frontend.
Booleancheckbox—
ChoiceselectLabels through enumDisplayNames.
Step inputstepFiltering and quick-create on the input binding.
List of texttokensOptional hook on the list's widget.
List of step inputsstepsInput settings under list.
Other listlistElement bindings under list.
RecordrecordFields under record.fields.

The complete widget vocabulary is text, textarea, code, command, number, checkbox, select, tokens, list, step, steps, record, and datetime. It is not an arbitrary browser-component name. code requires language, and command requires prefix:

bindings.body.widget = { kind = "code"; language = "python"; };

For a text parameter called extraArgs, a command-style control would be:

bindings.extraArgs.widget = { kind = "command"; prefix = "fastqc"; };

The prefix is a display hint, not a command automatically prepended to the build. The template still decides how to use the value. Widgets must match the field's shape in both the stdlib and frontend; in particular, use number, not text, for integer arguments. Only the text and tokens widgets accept a hook.

Read-only fields and paths

A text, integer, or boolean binding may set readOnly = true. A path list tells the frontend where to read the value inside args, as with the generated download timestamp. This only changes the form: it does not add an argument or enforce an edit restriction on the backend.

There is no visible = false option system in this API. Values supplied by Pointy, such as args.id and upload/download store references, are handled by the stdlib rather than declared as hidden form options.

Autocomplete

Hooks return JSON-serializable lists of strings. This template-level hook works with packages.widget above:

autocomplete = { pkgs, lib }: {
  packages = { query ? "", limit ? 25, ... }:
    lib.take limit
      (lib.filter (lib.hasPrefix query) (builtins.attrNames pkgs));
};

A token list names the hook on its own widget: { kind = "tokens"; hook = "packages"; }. If the list widget has no hook, the frontend falls back to the hook of its text element. Scalar text bindings also accept autocomplete = "hookName", which supplies the hook when the default widget is selected. An explicit widget replaces that default, so include its hook explicitly when needed.

The frontend sends query, limit, and simple text/choice values from the surrounding record as context. Accept those values in the function's argument set, or use ... if you do not need them. limit defaults to 25 and is clamped to 1–100, and the query and context values may only contain letters, digits, and _.+-. Suggestions do not restrict the final value: users may enter their own strings.

The generated form document

nix eval --json .#pointy.stepConfig produces a document with this structure:

{
  "version": 4,
  "templates": {
    "hello": {
      "displayName": "Greeting",
      "description": "Write a message into a result file.",
      "sortKey": 0,
      "icon": "description",
      "kind": "derivation",
      "withSrcFiles": false,
      "fields": [
        {
          "name": "message",
          "label": "Message",
          "help": "Text to put in message.txt.",
          "readOnly": false,
          "required": false,
          "default": "Hello, research!",
          "widget": { "kind": "textarea" },
          "shape": { "kind": "text" }
        }
      ]
    }
  }
}

Each field has one widget and one value shape. In the JSON, shape kinds are text, int, bool, choice, artifact, list, and record. A choice includes options with value and optional label; an artifact (step input) includes accepts, proven, and create; a list includes an element with its own widget and shape; a record includes a fields array whose entries carry required: true and default: null. Upload entries use kind = "upload" and accepts; download entries use kind = "download" and add the read-only downloaded timestamp field.

The language argument-schema version is 4, and the form-document version is also 4; they are different documents. Custom clients should consume the form document rather than guessing controls from template names. See Using the Backend API.

Builder helpers

pointy-stdlib.lib provides mkFlake, loadDir, csvExtras, fastqExtras, nixDepPkgs, and stepLinkCommands.

  • nixDepPkgs { pkgs; nixDeps; } resolves a list such as [ "python3" "jq" ] to packages. Unknown attribute paths fail evaluation with Unknown Nix package `<name>`.
  • stepLinkCommands { stepDeps; } produces shell commands linking resolved upstream outputs into the working directory under their step ids.
  • csvExtras and fastqExtras take { pkgs; baseDrv; requirements?; } and return metadata scanner derivations.

For resource precedence, notices, metadata, source files, and extras layout, see the corresponding sections of Setting Up the User Repository.