Skip to content
Talk to our solutions team

Languages

Seven runtimes. The language is chosen by file extension, and --runtime overrides that when the extension is missing or wrong.

Extension--runtimeShape it expects
.jsjavascriptA function, called by name
.lualuaA function, called by name
.starstarlarkA function, called by name
.gogoAn exported function — Main, not main
.exprexprA single expression
.celcelA single expression
.wasmwasmA compiled module

The flag’s help text mentions values like v8 and goja. Those are rejected:

$ kis script compile hello.js -r v8
compilation failed: unsupported language: v8

So is the abbreviation js. Use javascript.

JavaScript, Lua and Starlark work the same way: define a function, the engine calls it, the return value is the result.

function main(name, count) {
return { greeting: `hello ${name}`, count: count };
}
function main(name, count)
return { greeting = "hello " .. name, count = count }
end
def main(name, count):
return {"greeting": "hello " + name, "count": count}

All three receive positional arguments and reach the host namespaces.

The Go runtime calls an exported function, so the entry point is Main with a capital M:

package main
func Main(x int) map[string]any {
return map[string]any{"doubled": x * 2}
}

A lowercase main produces:

execution failed: function 'main.Main' not found: undefined selector: Main

The same applies to any entry point named with --func — export it.

expr and CEL evaluate one expression. There is no function, no statement, no entry point:

$ echo '1 + 1' > calc.expr
$ kis script run calc.expr --debug
Result: 2

They are built for embedding, and that is where they earn their place: a computed field, an access rule, a routing condition. In those positions the host supplies the environment — the row, the request, the record — and the expression names it directly.

At the CLI there is no host, so there is nothing to name. --vars and positional arguments do not declare variables for these two, because compilation happens before values bind:

$ kis script run doubler.expr --vars x=21
compilation failed: expr compilation error: unknown name x (1:1)

So use the CLI for these the way it is useful: checking that an expression parses and that a constant folds the way you expect, before pasting it into the schema or rule that will supply its environment. For anything needing input at the CLI, write JavaScript.

A .wasm file is a compiled module, built elsewhere and executed here. Reach for it when the work is CPU-heavy enough that interpretation costs real time, or when the logic already exists as a compiled artifact.

There is no source for the engine to check, so validate confirms only that the binary is a well-formed module.

Compiling one file under several languages is a quick way to see what each one is:

$ kis script compile hello.js -r lua
error: hello.js line:1(column:17) near '{': syntax error
$ kis script compile hello.js -r expr
error: unexpected token Identifier("main") (1:10)
$ kis script compile hello.js -r wasm
error: invalid magic number

Each message names the real reason: Lua does not use braces that way, expr wants an expression rather than a declaration, and wasm expects a binary rather than text.

Starlark accepting a JavaScript-shaped function is not permission to write JavaScript in it. The syntaxes overlap far enough to compile and diverge immediately afterwards.

The scriptLanguage
Calls host namespaces and has real control flowjavascript
Is untrusted or model-generated, and should be constrained by constructionstarlark
Is one condition or one calculation, embedded in a schema or ruleexpr or cel
Is CPU-heavy and compiled elsewherewasm
Is maintained by a team that already writes Lua or Golua or go

JavaScript is the default for a reason: it has the widest namespace coverage, the most examples, and the least friction. Choose something else when you have a reason, not a preference.

Starlark’s reason is worth spelling out, and worth being precise about. The language has no I/O of its own: no clock, no filesystem, no network, no unbounded recursion. Everything it can reach, the host handed it — which means the reach is something you decide rather than something you have to audit for.

That is not automatic. By default kis script grants every namespace, to Starlark as much as to JavaScript:

def main():
return shell.execute({"script": "echo hi", "capture": True})

runs, and shells out. The guarantee comes from pairing the language with an explicit grant:

Terminal window
kis script run generated.star --namespaces none

Now the language contributes no authority and the host contributes none either. For logic you did not write and cannot fully review, those two together are a stronger position than restricting a more capable language after the fact. See What a script can reach.

Terminal window
kis script compile transform.js --runtime javascript
Compiling with javascript runtime...
✓ Compilation successful (95.377µs)
Source size: 59 bytes

Compile under the language you will actually configure, not the one you assume. A file that compiles as javascript says nothing about whether it compiles as starlark.