flarisvm hello.fls

Flaris
a compact
async scripting language

Run scripts straight from the command line, fold a program and the runtime into a single native executable, or embed the VM in an application that needs scripting. Fiber-based async/await on a compact bytecode VM written in C - with a 32-module standard library, zero dependencies, and 46+ open-source add-on libraries.

Fiber-based concurrency Static analyzer FFI / C plugins Embeddable VM ARM64 & x86_64 JIT Package manager Windows · Linux · MacOS Portable bytecode Null safety Optional typing
↓ Download View examples Documentation - GitHub ↗
hello.fls
// A quick taste of Flaris
import { Https, Status, ContentType, CookieJar, Multipart } from library("Https", "1.0");

fn async fetchData(url:string):object {
    var resp = await Https.GetAsync(url);
    return Json.Parse(resp.body);
}

fn async Main() {
    let data = await fetchData("https://api.example.com/data");
    Console.WriteLine("Received: ", Json.Stringify(data));

    // Fibers run concurrently on the cooperative scheduler
    let jobs = [
        fetchData("https://api.example.com/a"),
        fetchData("https://api.example.com/b")
    ];
    foreach (j in jobs) await j;
    return 0;
}

Why Flaris

Built for speed, designed for clarity

Flaris is a compact scripting language with a fast interpreter, an automatic JIT and first-class support for asynchronous programs. It performs in the same territory as Lua and QuickJS on many workloads, while offering familiar syntax, optional typing, portable bytecode and direct access to native libraries.

Run scripts directly during development, package them with the runtime as a single executable, or embed Flaris when an application needs scripting. The complete runtime is about a megabyte and has no external dependencies.

Bytecode VM

Compiles .fls source to compact .flx bytecode. Execute pre-compiled files for fast cold-start - compile once, deploy anywhere.

🔀

Fiber Concurrency

Lightweight fibers with async/await and cooperative scheduling. No threads, no data races - deterministic execution always.

🔍

Static Analyzer

A multi-pass analyzer (10 passes) catches type errors, unused variables, unreachable code, and flow issues before runtime.

🔌

FFI / C Plugins

Load native shared libraries at runtime and call C functions directly. Wrap performance-critical code in a thin C layer with full access to the VM API.

📦

32 Stdlib Modules

JSON, Regex, Crypto, File I/O, Streams (TLS included), Collections, Reflection, and more - batteries included without external dependencies.

🛡️

Safe by Default

Unsafe features (FFI, raw memory, eval) require the explicit --unsafe flag. Any module import can be pinned to a SHA-256 fingerprint - if the bytecode doesn't match, the VM halts before execution. Production deployments stay locked down.

🌐

Cross-Platform Bytecode

Compile on macOS, run on Linux (ARM64 or x86-64) or Windows without recompiling. Portable .flx files run on any supported target.

🧩

Embeddable VM

Link libflaris into your C application and script your game, tool or server. Each script gets its own namespace, capabilities and budget, and nothing it does ends your process.


Embed the VM -
📦

Self-Contained Binaries

Fold your program and the runtime into one native executable with --embed - a Go-style single file to ship, ~1 MB total, no install on the target machine.


How it works -
🖥️

Familiar Syntax

Borrows from JavaScript and C# - let/var, classes, lambdas, modules. Short adoption time from most modern languages.

🚀

JIT Compiler

On by default: every eligible function compiles to native ARM64 or x86-64 automatically - no annotations, nothing to switch on. Loop-heavy math and array work runs 7-10x faster than the interpreter, same source. --jit-disable turns it off; --check reports why any function stayed interpreted.

🐞

Built-in Debugger

Breakpoints, stepping and variable inspection - in VS Code or from a terminal with --debug. The VM speaks the Debug Adapter Protocol itself, so there is no adapter to install.

🪶

Low Memory Footprint

SLAB allocator, SSO strings (≤7 chars inline), and compact bytecode keep heap pressure minimal. The full VM, compiler, and standard library weigh in at just over 1 MB - ideal for embedded targets and resource-constrained environments.

🏷️

Optional Typing

Annotate as much or as little as you want. The analyzer infers types where omitted and enforces them where declared. Null unions (string|nil) and null-coalescing (??) are built into the language.

📦

Package Manager

flarispm installs packages from any git repository - no central registry needed. Written in Flaris itself. One command to add, compile, and import a library.


Get flarispm →

Code Examples

See Flaris in action

From classes to fibers, from HTTP to native FFI - a few patterns to get started.

class Animal {
    let name = "";          // instance fields are declared in the class body
    fn Constructor(n: string) { this.name = n; }
    fn Speak() { Console.WriteLine(this.name, " makes a sound."); }
}

class Dog : Animal {
    fn Constructor(n: string) { super(n); }
    fn Speak() { Console.WriteLine(this.name, " barks."); }
}

fn Main() {
    let d: instance = new Dog("Rex");
    d.Speak();  // Rex barks.
    return;
}
fn async fetch(id: int): int {
    Fiber.Sleep(id * 10);
    return id * id;
}

fn async Main() {
    // Spawn three fibers - they run cooperatively
    let a: fiber = fetch(3);
    let b: fiber = fetch(1);
    let c: fiber = fetch(2);

    // await collects each result
    Console.WriteLine(await a, await b, await c);  // 914
    return;
}
import { Http, Status, ContentType, CookieJar, Multipart, HttpError, HttpUnsupportedError } from library("Http", "1.0");

fn Main() {
    // Simple HTTP GET
    let resp = Http.Get("http://httpbin.org/json");
    Console.WriteLine("status: ", resp.status);

    let data = Json.Parse(resp.body);
    Console.WriteLine(data.slideshow.title);

    // POST with JSON body
    let payload = Json.Stringify({ name: "Flaris", version: 1 });
    let post = Http.Post("http://httpbin.org/post", payload,
                 { "Content-Type": "application/json" });
    Console.WriteLine("posted, status: ", post.status);
    return;
}
fn divide(a: int, b: int): int {
    if (b == 0) throw(1, "division by zero");
    return a / b;
}

fn Main() {
    try {
        Console.WriteLine(divide(100, 10));  // 10
        Console.WriteLine(divide(100, 0));   // throws
    } catch (e) {
        Console.WriteLine("caught: ", e.Error);
    } finally {
        Console.WriteLine("cleanup");
    }

    // Null coalescing
    let raw: string|nil = nil;
    let val: string|nil = raw ?? "default";
    Console.WriteLine(val);  // default
    return;
}
// Tight numeric loops compile to native ARM64 or x86-64 at runtime.
// The JIT is on by default - nothing to annotate, nothing to switch on:
//   flarisvm compute.fls                 // JIT active
//   flarisvm --jit-disable compute.fls   // bytecode interpreter only
//   flarisvm --check compute.fls         // which functions qualify, and why not

fn sum(n: int): int {
    var s: int = 0;
    for (var i: int = 0; i < n; i++) {
        s = s + i;
    }
    return s;
}

fn Main() {
    Console.WriteLine(sum(1000));  // 499500
    return;
}

// Published cross-language kernels, Apple M5 (bench/results):
//   fib(38)      interpreter 1777 ms   JIT 181 ms   9.8x
//   sieve(10M)   interpreter  191 ms   JIT  29 ms   6.6x
//   collatz(1M)  interpreter 1500 ms   JIT 213 ms   7.0x
//
// Eligibility is decided from type annotations: typed scalars, typed
// arrays and blocks, string compare/concat, field access on a known
// class, and calls to other eligible functions. try/catch, await,
// closures and globals fall back to the interpreter transparently.
// Run:  flarisvm --debug app.fls
// Stops at the start of Main, then you drive it from the prompt.

[Entry] Main (app.fls:40) [fiber 1]
(fdb) b inspectOrder
breakpoint 1 at app.fls:21 (inspectOrder)
(fdb) c

[Breakpoint 1] inspectOrder (app.fls:21) [fiber 1]
(fdb) locals
  [0] order            = (instance) Order {3 fields}
  [1] retries          = (int) 2
(fdb) p order.label
order.label = (string) "espresso"
(fdb) n              // step over; s steps in, finish runs to the return
[Step] inspectOrder (app.fls:22) [fiber 1]
(fdb) fibers          // every fiber, and what each is waiting on
-> #1   running     frames=2    inspectOrder (app.fls:22)
   #2   yielded     frames=1    producer (app.fls:9)
   #3   wait-fiber  frames=1    consumer (app.fls:14)  waiting on fiber 2

// Also: bt, up/down, list, dis (bytecode), stack, info, delete, help.
// An uncaught exception drops you here too, at the throw site,
// with every frame still standing.
// Run:  flarisvm --check app.fls
// The static analyzer runs 10 passes and reports early:

fn Main() {
    let x: int = "hello";        // ← error: type mismatch
    let y: int = 42;

    return;
    Console.WriteLine("unreachable");  // ← warning: dead code
}

// app.fls:5:5:  1000: Type mismatch for 'x' (expected compatible with declared type).
// app.fls:5:5:  2000: Local 'x' is never read (prefix with '_' to suppress).
// app.fls:6:5:  2000: Local 'y' is never read (prefix with '_' to suppress).
// app.fls:9:36: 2006: Unreachable code.

Example Collection

Runnable .fls files - each covering a distinct language feature or use case.

01 Hello Console
02 Arrays, Objects & Functions
03 JSON Transform
04 Files & Hash Integrity
05 Classes & Reflection
06 Fibers & Async Pipeline
07 Command Arguments
08 Debugging
09 Switch & Control Flow
10 Error Handling & Guard
11 Math Utilities
12 Module Imports
13 Graphics
14 FFI / C Extensions

Debugging

A real debugger, in the VM

Set breakpoints in VS Code and step through Flaris source with locals, object fields and array elements in the Variables pane. The VM speaks the Debug Adapter Protocol itself — there is no separate adapter process to install. The same debugger is available as a terminal prompt with --debug, so a session over SSH needs nothing but the VM.

Debugging a Flaris program in VS Code. Left: the Variables pane shows locals, with an Order instance expanded to its id, total and label fields, and the call stack lists compute and Main. Centre: the source file with a breakpoint on the current line. Right: the Disassembly View listing the bytecode, with the instruction for the current line highlighted.
Stopped at a breakpoint in compute: locals with the Order instance expanded, the call stack, and the Disassembly View showing the bytecode for the same line.
🎯

Breakpoints & stepping

Gutter breakpoints, step in/over/out, and pause a running program. Breakpoints can be added or removed while it runs.

🧵

Every fiber is a thread

Fibers map exactly onto the editor's thread picker, so you can read any fiber's stack — and see what each one is waiting on.

🧩

Inspect anything

Locals, class instances, nested objects and arrays — including flat [float] arrays, read straight from their backing store.

💥

Post-mortem

An uncaught exception stops at the throw site with every frame intact, so you can read the values that caused it instead of guessing from a trace.

🔬

Down to the bytecode

The Disassembly View shows the instructions around any frame, and dis ir shows the typed IR a JIT-compiled function was built from.

🛡️

Read-only by design

Inspecting never allocates or changes a reference count, so attaching a debugger cannot perturb the behaviour you are trying to observe.

Debug from the editor, or from a terminal
# In VS Code: install the extension and press F5 on any .fls file.

# From a terminal - stops at the start of Main:
$ flarisvm --debug app.fls

# Serve the Debug Adapter Protocol on loopback for an editor:
$ flarisvm --dap=4711 app.fls

Debugging turns the JIT off. A JIT-compiled function runs natively and never reports its source lines, so a breakpoint inside one could not be reached — running everything in the VM is what makes stopping reliable rather than silently skipped.

Standard Library

32 modules, zero dependencies

Everything from JSON and cryptography to regex, TLS streams and reflection is built in. No import statement and no package manager required for the essentials — every module below is always in scope. Ffi and Memory additionally require --unsafe at runtime.

Data

  • Array
  • Buffer
  • Collections
  • Object
  • Class
  • Type

Strings & Text

  • String
  • Char
  • Regex
  • Json
  • Convert

Math & Crypto

  • Math
  • Random
  • Hash
  • Crypto

I/O & Files

  • File
  • Directory
  • Path
  • Stream
  • Console
  • FileWatch

Networking

  • Net

Concurrency

  • Fiber
  • Event
  • Scheduler
  • Timers

System

  • OS
  • VM
  • Ffi
  • Memory

Time & Debug

  • Time
  • Debug

TLS is not a separate module: Stream.ConnectTls and Stream.AcceptTls return ordinary streams, so every Stream-based library runs unchanged over an encrypted socket. HTTP clients and servers, compression, archives and 2D graphics are add-on libraries written in Flaris, not builtins.


Libraries

46 add-on libraries, ready to import

Install a library once into your per-user libs folder — ~/.flaris/libs on macOS and Linux, %USERPROFILE%\.flaris\libs on Windows — and import it by name from any script, no --libs flag needed. Every one is open source under the MIT licence and written in Flaris itself, so they double as a substantial worked example of the language.

Collections

Compression

Full descriptions, import lines you can copy, .fls sources and the FFI plugin shims live on the library index — searchable by name, purpose or class.

Browse all 46 libraries → Source on GitHub ↗

Downloads

Get Flaris

One command installs flarisvm and flarispm and adds them to your PATH.

macOS · Linux
$ curl -fsSL https://www.flaris-lang.org/install.sh | sh
Windows (PowerShell)
PS> iwr -useb https://www.flaris-lang.org/install.ps1 | iex
Getting started
# Run your first program
$ flarisvm hello.fls

# Compile to bytecode, then execute
$ flarisvm --compile hello.fls hello.flx
$ flarisvm --exec hello.flx

# Ship it as one self-contained binary
$ flarisvm --embed hello.fls hello

Installs to ~/.flaris/bin/ by default. Override with FLARIS_HOME=/your/path. Or download individual binaries below. flarisvm is the full toolchain (compiler + runtime); flaris is the compiler-free runtime — around a third smaller and the base for self-contained binaries. Note that flaris loads only signed, trusted bytecode — see Signing. If you want to run your own freshly compiled .flx, use flarisvm.

macOS flarisvm ARM64 · Apple Silicon ↓ Download Linux flarisvm x86_64 ↓ Download Linux flarisvm ARM64 ↓ Download Windows flarisvm.exe x86_64 · MSYS2 clang64 ↓ Download

Runtime only (flaris) — the small, compiler-free VM used to run .flx and as the stub for --embed.

macOS flaris ARM64 · runtime only ↓ Download Linux flaris x86_64 · runtime only ↓ Download Linux flaris ARM64 · runtime only ↓ Download Windows flaris.exe x86_64 · runtime only ↓ Download

Embedding Flaris in your own application? libflaris is the same VM as a linkable library — static and shared, with the headers staged alongside. See Embed the VM.

macOS libflaris ARM64 · .a + .dylib + headers ↓ Download Linux libflaris x86_64 · .a + .so + headers ↓ Download Linux libflaris ARM64 · .a + .so + headers ↓ Download Windows libflaris x86_64 · .a + .dll + headers ↓ Download

Editor support and the full documentation live under For Developers below.


Release

What's new

The headline changes in the current release. The changelog lists every new feature and fixed bug, release by release.

1.0.3.0 2026-09-15

Added

  • Multiple isolated script environments per VM. New FlarisCreateContext/FlarisDestroyContext embedding calls: each context is its own globals, capabilities and budget, sharing one VM and thread, cheap enough to spin up per request or per plugin.
  • String interpolation, $"...", and null-conditional access, obj?.prop / arr?.[i], short-circuiting to nil instead of raising.
  • Labeled loopsouter: for (...) { break outer; } — and >>>/>>>=, a logical right shift of the full 64-bit int.
  • --allow-ffi grants Ffi.* alone, without the rest of --unsafe; FFI handles are now a tagged opaque pointer type instead of a forgeable int.
  • Os.Execute can verify the binary before it runs — an optional sha256 argument refuses to launch on a mismatch.

Changed & fixed

  • The JIT is on by default--jit-disable turns it off; run untrusted bytecode with that flag.
  • ^^ now binds tighter than every operator and nests right: 2 ^^ 3 ^^ 2 is 512, not the old left-associative 64.
  • [int] arrays are stored flat and released int/ float boxes are cached per-VM — float-heavy code is 11-18% faster.
  • The JIT truncated ints outside ±2^60 stored into class fields and read back garbage; field loads and stores now detect the boxed form.
  • A call inside a new { ... } block to a same-named function re-entered the block instead of making a plain call, corrupting the new object.

The bytecode floor is now 1.0.3.0: a .flx built by an earlier version must be recompiled. The opcode set grew from 174 to 198 in this release. Read the full changelog →


Run it

One binary, the whole toolchain

Point the VM at a file and it runs — no project file, no build step, nothing to assemble beforehand. The analyzer, the formatter, the debugger and the timing and memory reports are all the same executable, so a machine with flarisvm on it has everything the language needs.

Everyday commands
# Compile and run in one step - anything after the file goes to the script
$ flarisvm app.fls data.csv --port=8080

# A one-liner, or a program piped in on stdin
$ flarisvm --eval 'Console.WriteLine(Math.Sqrt(2));'
$ cat app.fls | flarisvm -

# Analyze without running it, and format it
$ flarisvm --check app.fls
$ flarisvm --format-write app.fls

# Where the time and the memory went
$ flarisvm --time --mem app.fls
🔍

Mistakes before runtime

--check runs the full analyzer and exits without executing a line — type errors, unreachable code, unused symbols and flow problems, plus a report of which functions the JIT will take. -Werror makes warnings fatal and --diagnostics json emits machine-readable output, so a CI gate needs no extra tool.

🧹

Formatting, built in

--format-write formats in place and --format-check exits non-zero when a file is unformatted — a one-line CI gate. Nothing to install and no config file to argue about; --indent, --width and --tabs are the whole surface.

📈

Numbers on request

--time reports wall clock and idle time, --stats adds execution counters, and --mem accounts for every object at shutdown — so a leak is a number in a report rather than a slow crash in production. --disasm prints the bytecode.

Or skip the compile at startup. Build once to portable .flx bytecode and run that instead. The same file runs on every supported platform, --strip drops the debug symbols from what you ship, and a corrupt or mismatched file is refused on load rather than half-run. It is the same bytecode the single-binary and embedded paths use.

Precompiled
$ flarisvm --compile app.fls app.flx --strip
Fingerprint(SHA256): [cd21bd4b3fcc...]

$ flarisvm --exec app.flx data.csv     # no compiler runs at startup

The debugger runs from here too — --debug for a terminal prompt, --dap for VS Code. Libraries come from flarispm, and every flag is in the Language Guide. When a script becomes something you hand to other people, fold it into a single executable; when it belongs inside a larger program, embed the VM.


Ship it

Self-contained binaries

Like Go, Flaris can fold your program and the runtime into a single native executable — one file to copy, nothing to install on the target machine. The full toolchain compiles your .fls, then appends the bytecode to the small flaris runtime stub. The result is an ordinary executable that runs your program when launched.

One command from source to binary
# Source in, self-contained executable out
$ flarisvm --embed app.fls myapp
$ ./myapp arg1 arg2          # runs standalone - no flaris on PATH, no --libs

# Statically link every library the program imports
$ flarisvm --embed app.fls myapp --bundle

# ...or name the exact files yourself
$ flarisvm --embed app.fls myapp --bundle='./mylib.flx'

# Already have bytecode? Embed the .flx directly
$ flarisvm --embed app.flx myapp

How it works. The program is appended to the runtime after its native image, with a small trailer recording where the payload begins. On startup the binary reads that trailer out of its own file and runs the embedded program — falling back to a normal VM only when no payload is present. Because the payload rides after the executable image, the OS loader ignores it, so a plain copy of the file still works.

Bundling. Pass --bundle and the compiler follows your import statements — and the imports those libraries make in turn — then links exactly that set into the payload. The shipped binary needs no ~/.flaris/libs and no --libs, and carries only the libraries it actually uses. To pick the set by hand, name the files instead: --bundle='./mylib.flx'.

The stub is the runtime only (no compiler, no eval), so you ship exactly the bytecode you built, with a smaller attack surface. A modest program lands around 1 MB total.

Self-contained binaries run on Linux, macOS (including Apple Silicon), and Windows — the payload rides in space every executable format already tolerates. Producing binaries that pass strict code-signing (macOS notarization, Windows Authenticode) is on the roadmap.

Embed the VM

Scripting for your application

The other direction: link libflaris into your own program and Flaris becomes its scripting layer — the way Lua is embedded. Load a script or a precompiled .flx, call its functions from C, and drive the scheduler from your own loop. It is the same VM the CLI runs, minus the command-line front end.

Cheap enough to use per request. Creating a context, loading a script, running it to completion and tearing it down costs tens of microseconds, so isolation does not have to be rationed: give every plugin, tenant or request its own namespace, capabilities and budget, and destroy it when the work is done. A script that misbehaves is contained, and a script that fails hands your code an error value instead of ending your process.

host.c
#include "flaris.h"

int main(void)
{
    FlarisInitVM(NULL);

    /* one script's own global namespace */
    FlarisContext *ctx;
    FlarisCreateContext(NULL, &ctx);

    FlarisLoadSourceText(ctx,
                     "fn Greet(who: string): string {\n"
                     "    return \"hello, \" + who;\n"
                     "}\n", "<host>");

    FlarisValue args[1] = { FlarisString("world") };
    FlarisValue result;
    char err[256];

    if (FlarisCall(ctx, "Greet", args, 1, &result, err, sizeof err) == FLARIS_OK)
        printf("%s\n", FlarisAsString(result, NULL));

    FlarisReleaseValue(result);
    FlarisReleaseValue(args[0]);
    FlarisShutdownVM(0);
    return 0;
}
Build and run
# macOS ARM64
$ cc host.c flaris-lib/lib/libflaris.a -Iflaris-lib/include -o host \
     -lm -framework Security -framework CoreFoundation

# Linux x86_64 / ARM64
$ cc host.c flaris-lib/lib/libflaris.a -Iflaris-lib/include -o host \
     -lm -ldl -lpthread

# Windows x64 (MSYS2 clang64)
$ cc host.c flaris-lib/lib/libflaris.a -Iflaris-lib/include -o host \
     -lws2_32 -lbcrypt -liphlpapi -lsecur32 -lcrypt32

$ ./host
hello, world

One header, one library. Every archive unpacks to the same shape. flaris.h is self-contained — it includes nothing but <stddef.h> and <stdint.h>, and declares the whole API in terms of C types and FlarisValue. Writing a C plugin that Flaris calls into is the other direction and uses ffi_object.h instead; embedding the VM does not need it.

flaris-lib/
include/
  flaris.h        # the embedding API - the only header, self-contained
lib/
  libflaris.a     # static: macOS, Linux, Windows
  libflaris.dylib # shared: macOS (libflaris.so on Linux; flaris.dll + libflaris.dll.a on Windows)

Static or shared, your call. The static library is the simplest thing that works — one archive, no runtime dependency to ship. The shared library exports exactly the API above and nothing else, so the VM's internals can never become something you accidentally depend on. Ship the library next to your executable and link it with an rpath so it is found there; on Windows, link the import library and define FLARIS_DLL so the declarations become dllimport:

Linking the shared library
# macOS - the rpath is what lets the exe find the dylib beside it
$ cc host.c flaris-lib/lib/libflaris.dylib -Iflaris-lib/include \
     -Wl,-rpath,@executable_path -o host

# Linux - same idea, $ORIGIN is the directory holding the exe
$ cc host.c flaris-lib/lib/libflaris.so -Iflaris-lib/include \
     -Wl,-rpath,'$ORIGIN' -o host

# Windows - link the import library, no rpath concept
$ cc host.c -DFLARIS_DLL flaris-lib/lib/libflaris.dll.a -Iflaris-lib/include -o host

Ship bytecode, not source. Compile your scripts to .flx as part of your build and load those instead — nothing to parse at startup, no script source in the shipped product, and a corrupt or mismatched file is refused on load rather than half-run. Bytecode is portable, so one .flx runs on every platform your application does.

Your loop stays yours. FlarisPump() runs one non-blocking pass of the scheduler — timers, fibers and async I/O all make progress — and returns immediately, so the VM never decides how long your application blocks. Bound the work per frame with FlarisPump(NULL, 4), or name a single context to advance only that script's fibers and leave every other one queued where it was. When you have no loop of your own, FlarisRunToCompletion() hands the VM the whole thing until the work runs out.

Errors are values, and nothing kills your process. A script that throws does not print and vanish: the call returns FLARIS_ERR_RAISED and hands you the exception's code and message, and the VM stays usable afterwards. That holds for any failure inside the call — a division by zero or an unhandled runtime error included. The library never calls exit().

The host stays in charge. Signal handlers, worker threads, the JIT, capabilities, the stack and fiber limits and the module search path are all yours to set at startup — and an out-of-range value is refused rather than quietly clamped. Point FlarisSetLogHandler at your logger and the VM's diagnostics go there instead of stderr, so an embedded VM never scribbles on a console you did not want it to touch.

Static and shared libraries for every supported platform are in Downloads. Integration is one header and a library: flaris.h is self-contained, declaring the whole API in terms of C types and FlarisValue, a one-word opaque handle. The full guide — lifecycle, configuration, contexts, loading bytecode, value ownership and scheduler integration — is in doc/embedding.md. One caveat worth knowing up front: the VM state is process-wide, so a host runs one VM at a time, on one thread — several scripts share it, each in its own context.


Security

Every release is signed

Flaris bytecode (.flx) carries an embedded Ed25519 signature. The official standard library is signed with the Flaris signing key, and the matching public key is compiled into the runtime as a trust anchor - so signed code verifies with nothing to download or configure. A tampered .flx always fails to load. How strictly unsigned code is treated depends on which binary you run.

flarisvm, the full toolchain, is permissive by default so you can run code you just compiled: it warns about unsigned bytecode but executes it. Pass --require-signed to refuse anything not signed by a trusted key. Its trust store is per-user at ~/.flaris/trusted_keys, and you extend it with --import-key.

flaris, the compiler-free runtime, is built for locked-down deployment: the requirement is compiled in and cannot be switched off. It loads only bytecode signed by a trusted key. Its trust store is the system-managed /etc/flaris/trusted_keys (%SystemDrive%\ProgramData\Flaris\trusted_keys on Windows), read with no environment input so that whoever launches the process cannot redirect it — which also means --import-key is unavailable there and the file must be added by root. Official releases need no setup at all: they are signed with the key below, which is already a built-in anchor.

Official Ed25519 public key
flaris-lang.org  b6e2237413be79854985a1d4650ddefca8bc9c517964e6b9814b887bc6b779be

This is the canonical reference. Confirm your install matches it: the key shown when you inspect a library, the key compiled into your runtime, and the value above should all be identical. If they agree, the artifact was signed by the official Flaris key.

Verify a library
# Inspect any .flx - the signer and trust status are in the header
$ flarisvm --disasm ~/.flaris/libs/Jwt.flx
  Signature   : Ed25519 (valid, trusted signer)
  Signer      : flaris-lang.org
  Signed by   : b6e2237413be79854985a1d4650ddefca8bc9c517964e6b9814b887bc6b779be

# Machine-readable status: unsigned | signed|trusted|<key>|<signer>
$ flarisvm --sig-info ~/.flaris/libs/Jwt.flx

# Full toolchain: opt in to strict checking
$ flarisvm --require-signed --exec app.flx

# Runtime: strict always, no flag needed and none accepted
$ flaris --exec app.flx
  Package is unsigned
    This runtime loads only trusted bytecode: sign it with a trusted key,
    or add the signer to /etc/flaris/trusted_keys as root.

valid, trusted signer means the embedded key matched a trusted key; valid, untrusted signer means the signature is good but the key is not trusted (trust a third-party publisher by adding the key to ~/.flaris/trusted_keys for flarisvm, or to /etc/flaris/trusted_keys as root for flaris); INVALID means the file was altered after signing.


Package Manager

Extend Flaris with flarispm

flarispm is the official Flaris package manager - written entirely in Flaris itself. Install libraries from any public or private git repository, or straight from a signed .flx URL, in one command. No central registry. No build system. Packages land in ~/.flaris/libs, which the runtime already searches.

All platforms flarispm.flx Signed portable bytecode · the only artifact you need ↓ Download .flx

The one-line installer above already puts flarispm on your PATH, so there is nothing further to set up. It ships as a single signed .flx, and flarisvm verifies its flaris-lang.org signature on every run - so you can trust it regardless of how it reached you. Downloaded on its own, run it as flarisvm --exec flarispm.flx.

Setup
# Already installed by install.sh / install.ps1 - check it is there
$ flarispm version
flarispm 0.5.0

# Grabbed the .flx on its own? Verify it, then run it directly.
$ flarisvm --sig-info flarispm.flx
signed|trusted|b6e2…79be|flaris-lang.org
$ flarisvm --exec flarispm.flx version
Install and use a package
# Initialise a project manifest
$ flarispm init

# Add any package from GitHub (or any git host)
$ flarispm add https://github.com/stefansolid/flaris-test-package

# ...or a pre-compiled .flx straight from a URL
$ flarispm add https://www.flaris-lang.org/libs/Argparse.flx

# Packages install to ~/.flaris/libs, which flarisvm already searches -
# no --libs flag needed. Set $FLARIS_LIBS to install somewhere else.
$ flarisvm myapp.fls

# Commit flaris.json AND flarispm.lock; anyone who clones the project
# then reproduces your exact build - same commits, same bytes.
$ flarispm install

# Confirm nothing drifted (exits non-zero if it did - good in CI)
$ flarispm verify
myapp.fls
import { Greet } from library("Greet", "1.0");

fn Main() {
    Greet.Hello("John");   // Hello, John!
    Greet.Shout("it works"); // IT WORKS!!!
}
flarispm init Create flaris.json in current directory
flarispm add <url> [version] Clone, compile and register a package - or download a .flx
flarispm add <url> --key <pk> Require a signed .flx from exactly this Ed25519 publisher key
flarispm install Reproduce every dependency in flaris.json, verifying its pin
flarispm remove <url> Remove a package and its compiled files
flarispm list Show installed packages and their status
flarispm update [url] Fetch the latest and move the lock (all packages, or one)
flarispm verify Check the installed files against the lock; exits non-zero on drift
flarispm prune Remove packages nothing depends on any more
flarispm version Print flarispm version

Two files, and you commit both. flaris.json is yours: it says what you asked for — a branch, a tag, a URL. flarispm.lock is generated: it says what actually resolved — the exact commit, the source and byte hashes, the publisher's Ed25519 key, and which module files each package owns.

That split is what makes a build reproducible. "main" names different code next week; a commit does not. flarispm install checks out the recorded commit even after the branch has moved on, verifies every pin before anything reaches the import path, and never re-pins — only add and update do. Dependencies a package declares for itself are installed too, in dependency order, and two packages claiming the same module filename is a refusal rather than a silent overwrite.


For Developers

Learn everything

Editor tooling and the full documentation - language specification, standard library reference, and FFI guide - all maintained alongside the source.

VS Code Extension

Syntax highlighting, inline analyzer errors, build/run tasks, and the debugger - breakpoints, stepping and variable inspection. Search for Flaris in the Extensions pane, or install from a terminal with code --install-extension flaris-lang.flaris.


View on the VS Code Marketplace →

Source on GitHub

The standard libraries, their tests, the examples, the FFI plugin sources and the docs - MIT licensed, and written in Flaris itself.


github.com/flaris-lang/flaris →

Language Guide

Read top-to-bottom introduction: variables, classes, fibers, async/await, and the module system.


View guide.md →↓ .md

Language Reference

Full CLI flags, type system spec, built-in functions, operator precedence, VM limits, and stdlib API tables.


View reference.md →↓ .md

FFI Guide

Write C plugins, expose functions to Flaris, marshal types, and build high-performance native extensions.


View ffi.md →↓ .md

Required header for building FFI plugins:

↓ ffi_object.h

Embedding Guide

Link libflaris into your own application: load a script or a precompiled .flx, call its functions from C, pass values across the boundary, and drive the scheduler from your own loop. The opposite direction to the FFI guide.


View embedding.md →↓ .md

Required header for embedding the VM:

↓ flaris.h

Bytecode Specification

The complete .flx ISA manual: container format, Ed25519 signing, string pool, all 174 opcodes with stack effects and traps, loader validation rules, and an annotated hex dump. Self-contained - everything needed to write your own compiler, VM or tooling, with a companion format diagram.


View bytecode.md →↓ .md

Changelog

New features and fixed bugs in each release, newest first. See what's new for the 1.0.3.0 highlights.


View changelog.md →↓ .md

Contact

Get in touch

Bug reports, feature requests, collaborations, and questions are all welcome. For questions and general chat, the Discord server is the fastest way to reach us.

Join the Flaris Discord →
[ enable JavaScript to reveal the address ]

For bug reports, please include the Flaris version and a minimal reproducing script.


Commercial

Commercial use

FlarisVM is free to use and redistribute, including in commercial products. There are no per-developer, per-device, or runtime royalties.

For organizations that require additional assurance or long-term commitments, commercial services are available:

Technical evaluation & integration from €3,000
Commercial support from €2,500/year
Long-term maintenance (LTS) from €5,000/year
Source escrow & source access by agreement
Sponsored development Platform support, integrations and runtime features

Flaris is currently looking for its first production integrations. Early-adopter arrangements are flexible, with a focus on validating Flaris against real embedded and edge workloads.

Start a conversation →

You don't need a commercial agreement to ship FlarisVM. Commercial services are for organizations that need support, continuity, source access, or dedicated engineering.