Tooling — the vn CLI
download Download pagevn Start interactive REPL
vn run <file> Execute a Varian script
vn <file> Same as `vn run <file>` (back-compat shorthand)
vn fmt <file> Format a Varian script in-place
vn test [dir] [--filter <substr>] [--timeout <secs>] Run tests in a directory (default: .)
vn lint [path] [--only category] [--format json] Lint a file or directory
vn add <pkg> Record a dependency in varian.pkg
vn wrap <target> Generate a Varian wrapper for a foreign library
vn --help Show usage
Debug environment variables: VN_DEBUG_AST=1 prints the parsed AST before compilation;
VN_DEBUG_BYTECODE=1 prints the compiled bytecode disassembly.
Module loading: there is no import
vn run <file> (and the bare vn <file> form) automatically reads every .vn file in
vn_modules/ (if that directory exists in the current working directory) and
concatenates them as a prelude before your file's source, then compiles the combined
source as one program. There is no import/use keyword in the lexer at all — Zenith,
the comptime ORM, and queue.vn's helpers are simply always in scope as long as
vn_modules/ is present next to where you run vn from.
vn run / bare file
vn run examples/zenith_app_test.vn
vn examples/zenith_app_test.vn // identical
vn fmt
Formats a file in place. Implemented as a separate comment-preserving re-lexer
(src/fmt.c) — distinct from the parser used by lint/test/run, since formatting
needs to round-trip whitespace/comments that the AST throws away.
vn test
vn test tests/
Walks a directory for .vn files and runs each as a test file. Inside a test file, use
test "description" { ... } blocks (see examples/ and tests/ for real examples) with
the assertion globals assert_eq(a, b), assert_ne(a, b), assert_throws(fn) — all
ordinary globals, not a module. On failure, the actual error message (not just the test
description) is printed indented under the ❌ FAIL line, so you don't have to re-run
under a debugger to see why something failed.
--filter <substr> runs only tests whose description contains <substr>. --timeout
<secs> (fractional seconds allowed) caps each individual test; a test exceeding it is
killed at the next loop back-edge, marked ⏱️ TIMEOUT, and counted as a failure without
hanging the rest of the run. The summary line reports X passed, Y failed (Z timed out),
W skipped.
Mocking native module functions inside tests:
let saved = mock.intercept("sqlite", "query", |conn, sql, params| { return [] })
// ... test code that calls sqlite.query(...) and gets [] back ...
mock.restore("sqlite", "query", saved)
mock.intercept(type_name, method_name, fake_fn) swaps in fake_fn for any
(type, method) dispatch pair — the same dispatch table every native module
(sqlite, redis, string, etc.) is built on — and returns whatever was previously
bound there, so mock.restore(type_name, method_name, saved) can put it back exactly.
vn lint
vn lint examples/
vn lint src/foo.vn --only security
vn lint . --format json
AST-walking static analysis (not the fmt.c tokenizer — a real parse tree walk, the same
pipeline vn run/vn test use). Output is one line per finding:
path:line: [category] message. Categories:
- correctness: unreachable code (any statement after
return/break/continue/throw(...)in the same block), unusedletbindings, shadowed bindings (aletname that shadows one already declared in an enclosing scope). - security: hardcoded secrets (a
let/field named likesecret/key/token/password/api_keyassigned a string literal longer than ~12 chars), string- concatenated SQL passed directly tosqlite.query/postgres.query(the anti-pattern the comptime ORM indocs/ZENITH.mdexists to replace). - performance: N+1 query pattern (a
sqlite/postgres/rediscall found inside afor/whilebody), SQL containingSELECTpassed as a query argument with noLIMITclause.
--only <category> filters to one category; --format json emits machine-readable
output instead of the plain-text lines above.
Package Management & Registry (vn add, vn remove, vn install, vn update, vn search)
For detailed information on Varian's package registry architecture, transitive resolution, capabilities, and version ranges, see Constellation Documentation.
-
Add a package dependency:
vn add <pkg_name>Appends<pkg_name> = "latest"toconstellation.toml(initializing a new manifest if missing). -
Remove a dependency:
vn remove <pkg_name>Removes the package from the manifest, prunes its directory and any unused transitive dependencies undervn_modules/, and updates the lockfile. -
Install dependencies:
vn install [--frozen]Fetches direct Git and index-resolved dependencies recursively, extracts them tovn_modules/, and writes theconstellation.lockfile. The--frozenflag prevents index updates and fails if the lockfile is out of sync. -
Update dependencies:
vn updateForces fresh queries to Git/registry index to re-resolve matching versions, computes updated SHA-256 integrity hashes, and rewrites the lockfile. -
Search registry:
vn search <query>Queries the registry index for packages matching<query>(or"*"for all) and lists their registered versions. -
Wrapping foreign libraries:
vn wrap python:requestsIntrospects a Python module and writesvn_modules/<module>.vnwrapper functions callingpython.run(module, fn, args)(see Standard Library Docs).
Build Orchestration (Kiln — vn build)
Varian applications are assembled and compiled using Kiln. For detailed information on bytecode container formats, native AOT compile harnesses, and compilation caching, see Kiln Documentation.
-
Build a portable bytecode bundle:
vn buildAssembles all source files and compiles them into a fast, portableapp.vnbbytecode container (executable directly viavn app.vnb). -
Build a native standalone binary:
vn build --releaseCompiles Varian code directly to optimized C via AOT, compiles it with the host compiler, and links it againstlibvarian.ato produce a standalone executable binary.
Building from source
make # default: -g debug build, fast to rebuild — use while developing
make release # -O2 hardened build to ship/benchmark (see docs/SECURITY.md)
make asan # AddressSanitizer + UBSan build, for finding memory bugs
make clean
make release enables _FORTIFY_SOURCE=2, stack-protector/-clash protection, full
RELRO + immediate binding, a non-executable stack, and a position-independent executable
(ASLR). make asan builds with -fsanitize=address,undefined; run the suite under it
with ASAN_OPTIONS=detect_leaks=0 ./vn test tests/ to catch overflows and UB. Both do a
clean first so object files aren't mixed between flag sets.