Wardian docs

Command line

Wardian is one program, wardian. With no command it serves your apps. Every other command does one job and exits. Run wardian --help to see the list.

wardian [APPS_FOLDER]                serve the apps
wardian start [--at-login]           run Wardian in the background as a service, and open it
wardian stop                         stop the background Wardian
wardian status                       whether Wardian runs, how, where, and whether it starts at login
wardian promote APP [FOLDER]         copy an app from the working folder into FOLDER
wardian export APP [FILE] [--with-data]
                                     write an app as a .wardian file
wardian new KIND PATH                create a starter package: module, page or suite
wardian add COMPONENT... PATH        copy UI components into a package
wardian check PACKAGE...             check packages against the package format
wardian docs FOLDER                  write this documentation site as static files
wardian skills [FOLDER]              install the AI skills into FOLDER/.claude/skills
wardian key [export|import FILE]     where the master key is; back it up; restore it
wardian --version

Settings come from environment variables, not from flags. Settings and environment lists them all.

Exit codes#

CodeMeaning
0The command did its job. For check, new and add: no package has errors.
1The command failed, or a checked package has errors. The server also exits with 1 when it stops on its own, for example when its port is in use.
2Bad usage: a missing argument, an unknown kind or an unknown option. The server exits with 2 when it refuses to start (see serve).
3status only: Wardian is not running.
128 + nThe server was stopped by signal n: 130 for Ctrl-C, 143 for SIGTERM, 129 for SIGHUP.

serve#

wardian [APPS_FOLDER]

Starts the web server on ADDR (default 127.0.0.1:8000) and serves the apps.

wardian                         # serves ./data/apps, with every example app
wardian ~/my-apps               # serves ~/my-apps as it is
ADDR=127.0.0.1:8001 wardian     # another port

At start Wardian prints:

It also writes a line to standard error and to DATA_DIR/wardian.log for every start and every stop.

Wardian refuses to start, with exit code 2, in two cases:

Any first argument that is not a command or an option is taken as a folder to serve. So a mistyped command, such as wardian chek, starts the server on a folder called chek.

start, stop, status#

wardian start [--at-login] [--no-open]
wardian stop
wardian status

start hands Wardian to the system's service manager, so it runs without a terminal and comes back after a crash (ADR-2610081800): a launchd agent on macOS (studio.wardian), a systemd user service on Linux (wardian.service). Neither needs sudo. The service runs this program by its full path, with the data folder written in full, WARDIAN_NO_OPEN=1, and its output appended to DATA_DIR/wardian.log. --at-login also starts it when you log in; without it, a start at login set before is kept. start waits up to 15 seconds for Wardian to answer /api/status, then opens it in the browser (not with --no-open) and says where it runs. When this same Wardian already answers, start opens it and starts nothing; another Wardian on the port is named and left alone. Without launchd or systemd, start runs Wardian detached and keeps its process id in DATA_DIR/wardian.pid; it then does not come back after a crash or start at login.

stop unloads the service (launchctl bootout, or systemctl --user stop and disable) or ends the process in wardian.pid, and removes the service file. A Wardian started in a terminal is left alone; stop says where it answers.

status says whether Wardian runs, how (launchd, systemd, plain or a terminal), its address and version, the apps folder, and whether it starts at login. It exits 0 when Wardian runs and 3 when not. In a terminal all three print a short block; otherwise name: value lines:

running: yes
how: launchd
address: http://127.0.0.1:8000
version: 0.4.4
apps: /Users/you/Library/Application Support/Wardian/apps
at login: yes
log: /Users/you/Library/Application Support/Wardian/wardian.log

The service file is ~/Library/LaunchAgents/studio.wardian.plist with --at-login and ~/.config/wardian/studio.wardian.plist without (launchd loads every file in LaunchAgents at login), or ~/.config/systemd/user/wardian.service. WARDIAN_SERVICE_LABEL changes the name.

promote#

wardian promote APP [FOLDER]

Copies APP from the working folder (DATA_DIR/apps/APP) into FOLDER/APP. FOLDER defaults to ./apps, the example apps of the repository. Use it to ship an app you made or changed inside Wardian (ADR-2610071122).

The copy replaces the old one exactly:

Wardian prints one line per file (added, changed or removed), then the command to review the change. If nothing differs, it says the two already match. It also records a version named promote in the app's history.

$ wardian promote splunk-table
history: splunk-table version 12 (promote)
  changed  apps/splunk-table/apps/rows/app.js

review it with: git diff -- apps/splunk-table

DATA_DIR is read as for the server, so run promote from the same folder you start Wardian in. It exits with 1 if there is no such app in the working folder.

export#

wardian export APP [FILE] [--with-data]

Writes APP from the working folder as a .wardian file, the same file Download makes in the browser (ADR-2610071248). FILE defaults to APP.wardian in the current folder.

$ wardian export loan-planner --with-data
wrote loan-planner.wardian (<size> KB), with the app's data; keys, accounts and permission answers are never included

export always reads DATA_DIR/apps, not a folder you named when you started the server.

new#

wardian new KIND PATH

Creates a starter package at PATH. The last part of PATH is the package name. It must use letters, digits, -, _ or ., and must not start with .. PATH must not exist yet.

KINDWhat you get
moduleWebAssembly functions of numbers; Wardian builds the interface. app.json, app.wasm, src/lib.rs, Cargo.toml, build.sh, README.md
pageWebAssembly plus your own page. The module files, plus index.html, app.js and ui/ (theme, button, field, card, Arrange)
suiteSeveral sealed apps on one screen. suite.json, style.css, header.html, three parts under apps/ (input, text, output), text.wasm, the Rust source, and ui/ (theme, button, field, card)

Each template's text files have the package name filled in. After it writes the files, new runs wardian check on them and prints the report. The exit code is the check's.

$ wardian new page apps/hello
created a page package in apps/hello

apps/hello  (module with page index.html, 13 files, 40 KB)
  ok       follows the spec

next: read apps/hello/README.md, change it, then run `wardian check apps/hello`

To change the WebAssembly, run the package's build.sh. It needs the wasm32-unknown-unknown Rust target (Install and run).

add#

wardian add [--force] COMPONENT... PACKAGE
wardian add --list

Copies components of the Wardian library into PACKAGE/ui/. The package owns the copies and may change them. theme.css always comes too, because every component reads its tokens. Components and Arrange shows each one.

ComponentWhat it is
buttonbuttons in five variants and three sizes
fieldtext inputs, selects, text areas, labels and hints
carda bordered box with a title, a description, content and a footer
badgea small label for a state or a count
tablea data table with a sticky header and right-aligned numbers
switchan on/off switch made from a checkbox
tabstabs with arrow-key movement between them
dialoga modal dialog, and WardianUI.confirm()
toastshort messages in the corner: WardianUI.toast()
tooltipa hint on hover and keyboard focus
progressthe <wardian-progress> bar (Wardian also provides it built in)
arrangeArrange: each viewer may reorder, move and hide the panels marked data-panel

What add does depends on the kind of package:

A file that is already in ui/ is kept, and add says so. --force (or -f) replaces it. After copying, add runs wardian check on the package; the exit code is the check's.

$ wardian add table apps/notes
  wrote    apps/notes/ui/table.css
  kept     apps/notes/ui/theme.css (already there; --force replaces it)
  added to suite.json styles: ui/table.css

apps/notes  (suite, 3 apps, 18 files, 24 KB)
  ok       follows the spec
wardian add button tabs toast apps/my-suite
wardian add arrange apps/my-page
wardian add --list

check#

wardian check PACKAGE...

Tests packages against the package format. It reads files only: it never runs the app. Each argument may be:

ArgumentWhat is checked
a package folder (holds app.wasm or suite.json)that package
a folder of packages, such as data/appsevery package in it, in name order
a .wardian, .zip or .rustle fileeach app inside, unpacked by the real importer into a temporary folder, exactly as an import would

For each package check prints a title line with a summary, then one line per error and per warning, then ok if there are no errors:

$ wardian check apps/loan-planner
apps/loan-planner  (suite, 6 apps, 23 files, 44 KB)
  ok       follows the spec
$ wardian check data/apps/notes
data/apps/notes  (suite, 3 apps, 18 files, 24 KB)
  error    suite.json: apps[0] (text): needs "output.load", but output does not provide "load"
  warning  suite.json: apps[0] (text): listens to "note:saved", which no app in the suite emits

The exit code is 0 when no package has errors and 1 otherwise. Warnings do not fail a check.

check cannot run JavaScript, so it does not compare the contract in app.js with suite.json. The kernel does that when the suite starts. Troubleshooting lists the common messages and what to do about each.

docs#

wardian docs FOLDER

Writes this documentation site as static files into FOLDER, the same pages Wardian serves at /docs:

$ wardian docs website
wrote <n> files of the docs site into website

The project website keeps its copy in website/. Contributing says when to run it.

skills#

wardian skills [--force] [FOLDER]
wardian skills --list

Installs the AI skills Wardian ships into FOLDER/.claude/skills/ (default: the current folder): wardian-app-factory and wardian-app-doctor, each with references/, the docs pages it needs. A file that is already there and differs is kept, and listed, unless you give --force. A file that is already current is left alone. Build with an AI assistant explains the skills.

$ wardian skills
2 skills in ./.claude/skills: 24 file(s) written, 0 kept, the rest already current

key#

wardian key
wardian key export FILE [--force]
wardian key import FILE [--force]

The master key seals every saved key (Keys and Claude settings). wardian key says where it is and how many saved keys it opens. export writes it to FILE, readable by you only, and keeps a file that is there unless --force; - prints it. import puts the key from FILE (-: standard input) back in its place, and refuses a key that opens none of the saved keys, or one that replaces another, unless --force. Exit code 1 when it is refused (ADR-2610081700).

--version and --help#

$ wardian --version
Wardian 0.4.0 (package format 2)

-V is the same as --version. --help, -h and help print the usage text. Any other argument that starts with - is an unknown option: Wardian prints the usage text and exits with 2.

rustle#

Wardian was called rustle before version 0.4. The rustle program is still built. It runs the wardian program in the same folder, with the same arguments, and exits with its exit code. If wardian is not next to it, rustle says so and exits with 1.

rustle check apps/adder      # the same as: wardian check apps/adder

This page is docs/site/cli.md in the repository. Something wrong or missing? Change that file.