Dispatch & applets
Dispatch is how the binary decides which applet runs. fw.Main() takes
no parameters by design — the argument vector is platform-sourced: os.Args
on POSIX, and in Windows service mode the vector the Service Control
Manager hands to Execute.
Single-applet mode#
With exactly one applet registered, there is nothing to decide — it is always dispatched, and selector logic is fully off:
argv[0]is ignored; binary name, symlinks, install location are all irrelevant.- The entire argument vector after the binary name belongs to the applet
as ordinary flags and positionals.
mytool myapplet --argsdoes not treatmyappletas a selector even if it happens to match the applet's id — it is a leading bare token under normal parsing, so there is no "data or selector?" ambiguity to reason about.
Only dispatch is simplified. The applet's id still anchors everything
else — the MYAPPLET_ env prefix, the config file names, closure
resolution, and the lifecycle all proceed as usual.
Selector rules (multi-applet)#
With two or more applets registered, two rules decide, in order:
- If the first argument exists and does not start with
-, it is always an applet selector. Look it up, dispatch with the remaining arguments. An unknown name is a dispatch failure — even ifbasename(argv[0])would itself name a valid applet. No fallback. - Otherwise,
basename(argv[0])must name a registered applet — the busybox symlink style. On Windows the.exesuffix is stripped before matching.
$ tbx upper --trim < notes.txt # rule 1: first bare argument selects $ upper --trim < notes.txt # rule 2: argv[0] selects (a symlink to tbx) $ tbx frobnicate # unknown selector: usage + applet list, # exit non-zero — no argv[0] fallback
The consequence, worth stating bluntly: in a multi-applet binary a
leading bare token is never applet data. Rule 1 runs first even when
the binary was invoked through a symlink — so upper notes.txt does not
hand notes.txt to the upper applet; it fails dispatch looking for an
applet named notes.txt. Scripts targeting multi-applet binaries keep
positionals behind a flag or after --, and nobody gets surprised.
The contract change to plan for#
Registering a second applet re-enables selector logic — and that
changes the binary's command-line contract: yesterday
mytool report.txt was a positional, today it is an unknown-applet
dispatch failure. That hard flip is deliberate. Mixing and matching
applets is a core idea of the framework — but an applet written assuming
it owns the whole command line must not silently survive gaining a
sibling. By failing loudly instead of guessing, the framework makes sure
the composition is a conscious decision: code destined for multi-applet
binaries is written — and its invocations are written — expecting the
selector contract from the start. The busybox demo
shows that side in practice.
Dispatch failures, `--help`, and listing applets#
Every dispatch failure — unknown selector, unmatched argv[0], or a
binary with zero registered applets — prints usage to stderr, including
the list of registered applet ids, and exits non-zero. In single-applet
mode the applet list is dropped from that output.
--help is deliberately different: it renders only the dispatched
applet's argument schema — core + its closure, grouped by service id —
and never an applet catalog. A core argument for enumerating applets
(something like --applets) is planned but not yet built; today the
list only appears in dispatch-failure usage output. Programmatically,
though, the enumeration exists: the core Introspector's Applets() —
see Services & injection.
Positionals#
- Every bare token after the last flag argument is collected as a positional.
- A bare token followed by another flag is a strict-parse error — positionals must come last.
- A literal
--ends flag parsing; everything after it is positional, dashes and all.
In v1 positionals are collected and exposed via fw.Positionals()
— nothing more. Parsing and routing them (per-applet positional schemas)
is deliberately deferred.
Related: the busybox demo for the practice, the lifecycle page for what happens after dispatch, and the design spec for the rules verbatim.