f Fogu

Official documentation

The Fogu language

Fogu (.fg files) is a small, predictable scripting language, dynamically typed with optional type annotations, built for automation, infrastructure, systems integration and small services. The reference implementation is written in Rust and ships an interpreter, a JIT compiler (Cranelift), a REPL, a CLI, a package manager and standalone executables.

Simple

A small grammar, optional semicolons, no implicit coercions.

Predictable

Errors are values, lexical scope, no hoisting, names and types checked before running.

Practical

HTTP, TCP/UDP, SQL, key-value, cron and events in the standard library.

Installation

Requires Rust (2024 edition) and git on the PATH (for packages).

First steps

Variables and types

let creates a mutable variable; const an immutable binding (the referenced object may still change). Scope is lexical, each { } block creates a new scope and there is no hoisting. Declaring the same name twice in the same scope is an error.

Conversions and truthiness

  • There are no implicit coercions: "10" + 2 is an error. Use int("10"), float(10), str(10).
  • The only automatic promotion is int → float in arithmetic: 1 + 2.5 == 3.5.
  • Only false and nil are falsy; 0, "", [] and {} are truthy.
  • == compares lists and maps by structure; structs by identity (or with their equals method).

Type annotations

Annotations are optional. They are checked at runtime (parameters on entry, return values on exit, let/const on assignment) and unknown type names are rejected before the program runs. Annotated numeric functions are still compiled by the JIT.

Built-in type names: int float str bool nil duration list map tuple fn chan error any, plus struct and interface names. error also accepts nil. Generic arguments such as list<int> document the intent; only the outer kind is checked.

Strings and template strings

Template strings use backticks and interpolate any expression with ${...}. They may span several lines.

str methods: split upper lower trim replace starts_with ends_with repeat lines chars slice.

Durations

Literals with a unit (ns us ms s m h d, decimals allowed) create duration values, stored in nanoseconds. They are accepted by time.delay, time.sleep, cron.every and timeout.

Fields: ns, us, ms (int) and seconds, minutes, hours (float).

Operators

Precedence (high → low)Meaning
() [] . ?.call, index, field, optional access
! - <-negation, unary minus, channel receive
**power (right-associative)
* / % · + -checked arithmetic (overflow and division by zero are errors)
< <= > >= · == !=comparison
&& · || ??logical; a ?? b returns b if a is nil
..half-open range [a, b)
= += -= *= /= %=assignment (also to fields and indices)

Control flow

No parentheses around conditions. match is an expression, with literal patterns, | alternatives, bindings with guards and the _ wildcard. A match must be exhaustive: without a _ (or plain name) arm it is rejected before the program runs.

Functions

Functions are first-class values with closures. There are three forms: declaration, anonymous function and arrow function. Parameters may have default values (which may use earlier parameters) and the last one may be variadic. Functions and structs may be generic; type parameters are erased at runtime.

Missing arguments are nil (or their default value); passing too many is an error, except for variadic functions.

Lists and maps

  • Lists: push pop shift unshift insert remove first last contains index_of join reverse slice concat sort map filter each find any all reduce
  • Maps: keys values entries has get set remove merge
  • All of them work as methods (xs.push(1)) or as functions (push(xs, 1)).

Errors as values

There is no try/catch. A function returns value, error; the ? operator returns the error to the caller. An error has message, cause and trace. ? may only be used in functions whose declared return type includes error (or that are not annotated).

The operational errors of native functions (a file that does not open, a refused connection, invalid SQL or JSON) are error values too. Programming errors (for example, an argument of the wrong type) stop the program.

Note: const [a, b] = f() destructures lists. If a native function returns a list (for example json.parse("[1, 2]") or db.query(...)), use ? instead of destructuring.

panic("msg") signals an unrecoverable failure. In an fg task it does not stop the other tasks, unless the program runs with --strict.

Structs, interfaces and composition

There is no inheritance: reuse comes from embed. The constructor is called new and methods reach the instance through this. Structs compare by identity, unless they define equals. Interfaces are structural: any struct (or map of functions) with the required methods satisfies them, without declaring it.

Modules and visibility

Each file is a module, evaluated only once. Use export to export functions, let/const, structs and interfaces. Names starting with _ are private to the module: they cannot be imported, read through module._x, or used as fields or methods of a struct outside the module where it was declared.

Concurrency: fg, chan, select

fg starts a cooperative task (not an OS thread). Buffered or unbuffered channels connect tasks; select waits for the first ready case (choosing randomly among ready ones) and timeout(d) returns a channel that receives a value after d. The runtime detects deadlocks.

Standard library

Modules are imported with import name from "fogu:name" (or by name: import { parse } from "fogu:json"). Each section below lists every function with its parameters and return value, and shows an example with its real output (the examples are run when this page is generated).

Conventions

Errors are values. Operational failures — a missing file, a refused connection, a server error, malformed input — come back as an error value: handle them with ? (propagate) or const [value, err] = f(). Programming mistakes — a wrong argument type, an unknown option — stop the program with a message. Options are passed as a last map argument: f(x, { timeout: 5s }). Durations accept a duration literal (500ms, 2s, 1h) or an int number of milliseconds. Network operations wait cooperatively: other fg tasks keep running while one waits.

Global functions

Available everywhere, without an import.

FunctionReturnsDescription
print(...values)nilWrites the values separated by spaces and a newline (Fogu.write writes without them).
len(x)intLength of a str (characters), list, map or chan (queued items).
typeof(x)strint float str bool nil list map fn duration chan error struct …
str(x)strText form of any value (the same text print shows).
int(x)intFrom a float (truncates), a numeric str or a bool. Invalid text stops the program.
float(x)floatFrom an int or a numeric str.
clock()floatSeconds since the UNIX epoch, with fractions — use differences to measure time.
error(message, cause?)errorA new error value; err.message, err.cause.
panic(...values)—Stops the program with a message (for impossible situations).
timeout(d)chanA channel that receives once after d (use with select).
close(ch)nilCloses a channel: receivers get nil once it is empty.

Strings, lists and maps

Methods of the built-in values. Indexes start at 0; negative indexes count from the end in slice.

str

FunctionReturnsDescription
s.len()intNumber of characters.
s.upper() · s.lower()strCase conversion.
s.trim()strWithout leading and trailing whitespace.
s.split(sep)listParts between sep.
s.lines() · s.chars()listLines (without line endings) · single characters.
s.contains(t) · s.starts_with(t) · s.ends_with(t)boolSubstring tests.
s.index_of(t)intCharacter index of the first t, or -1.
s.replace(old, new)strEvery old replaced.
s.repeat(n)strs repeated n times.
s.slice(start, end?)strCharacters from start up to (not including) end.

list

FunctionReturnsDescription
l.push(x) · l.pop()nil · valueAdd to / remove from the end.
l.unshift(x) · l.shift()nil · valueAdd to / remove from the start.
l.insert(i, x) · l.remove(i)nil · valueAt an index.
l.first() · l.last()valuenil when empty.
l.contains(x) · l.index_of(x)bool · intSearch by value.
l.join(sep)strItems as text joined with sep.
l.reverse() · l.sort(fn?)listNew list; sort takes an optional comparator (a, b) => int.
l.slice(start, end?) · l.concat(other)listCopies.
l.map(fn) · l.filter(fn) · l.find(fn)list · list · valueFunctional helpers.
l.any(fn) · l.all(fn) · l.each(fn)bool · bool · nil
l.reduce(fn, initial)valuefn(acc, item) over the items.
l.clear() · l.len()nil · int

map

FunctionReturnsDescription
m.keys() · m.values() · m.entries()listEntries are [key, value] pairs.
m.has(key)bool
m.get(key, default?)valueThe value, or default (nil).
m.set(key, value) · m[key] = valuenil
m.remove(key)valueRemoves and returns the value.
m.merge(other)mapA new map; other wins on equal keys.
m.len() · m.clear()int · nil

The Fogu object

Runtime helpers available everywhere, without an import.

FunctionReturnsDescription
Fogu.version · Fogu.osstrFogu's version · windows, linux, macos…
Fogu.envmapEnvironment variables.
Fogu.argslistArguments after the script name (fogu run app.fg a b).
Fogu.file(path)Filetext() lines() json() write(s) append(s) exists() size() remove(), fields path name ext dir.
Fogu.asset(path)strA project file: relative to the project root — and inside fogu build --compile executables, the extracted embedded files.
Fogu.run(cmd, args?, opts?)mapRuns a program → { ok, code, stdout, stderr } (see fogu:process).
Fogu.glob(pattern)listFiles matching *, ? and **.
Fogu.hash(data, algo?)strHex digest, sha256 (default) or sha512.
Fogu.sql(url)DatabaseSame as sql.open(url).
Fogu.sleep(d)nilWaits (other tasks keep running).
Fogu.write(...values)nilLike print, without spaces or newline (streaming output).
Fogu.watch(path | [paths], { recursive = true, debounce = 50ms })Watcher · errorFile system events: w.next(timeout?) → { kind, path, paths } (create modify remove rename), tryNext(), close().
Fogu.exit(code?)—Ends the program.

Watching files — rebuild on every change:

fogu:math

FunctionReturnsDescription
math.PI · math.EfloatConstants.
math.abs(x)numberAbsolute value (int stays int).
math.sqrt(x)floatSquare root.
math.pow(base, exp)numberPower.
math.floor(x) · math.ceil(x)intRounding down / up.

fogu:json

FunctionReturnsDescription
json.parse(text)value · errorJSON text → maps, lists, str, int, float, bool, nil. Invalid JSON is an error value.
json.stringify(value)strCompact JSON (map keys in order).

fogu:time

FunctionReturnsDescription
time.now()intMilliseconds since the UNIX epoch.
time.Date()DateThe current instant (UTC).
time.delay(d) · time.sleep(seconds)nilWaits; other fg tasks keep running.

Date

FunctionReturnsDescription
d.year · d.month · d.dayintMonth 1–12.
d.hour · d.minute · d.second · d.millis · d.weekdayintWeekday 0 = Sunday.
d.getTime()intMilliseconds since the epoch (also d.ms).
d.toISOString()str2026-10-10T12:00:00.000Z
d.toString()strHuman readable.
d.addMs(ms)nilMoves the date by ms (in place).
d.diff(other)intMilliseconds between the dates.
getFullYear() getMonth() getDate() getDay() getHours() getMinutes() getSeconds() getMilliseconds()intJavaScript-style getters (month 0–11).

fogu:system

FunctionReturnsDescription
system.argslistScript arguments (fogu run app.fg a b → [a, b]).
system.time()floatSeconds since the epoch.
system.exit(code)—Ends the program.
system.info.os · .arch · .familystrwindows/linux/macos · x86_64/aarch64 · windows/unix.
system.info.cores · .pidintLogical CPUs · process id.
system.info.env(name)str · nilAn environment variable.
system.info.sleep_ms(ms)nilBlocks the whole program (prefer time.delay).

fogu:fs

Files and folders. Failures (missing file, permission) are error values.

FunctionReturnsDescription
fs.readText(path)str · errorWhole file as UTF-8 text.
fs.readLines(path)list · errorLines without line endings.
fs.writeText(path, text)nil · errorCreates or replaces.
fs.append(path, text)nil · errorAdds to the end (creates the file).
fs.exists(path) · fs.isFile(path) · fs.isDir(path)bool
fs.mkdir(path)nil · errorCreates the folder and its parents.
fs.list(path)list · errorNames inside a folder, sorted.
fs.stat(path)map · error{ size, isFile, isDir, modified } (modified: ms since the epoch).
fs.copy(from, to) · fs.move(from, to)nil · error
fs.remove(path)nil · errorA file or an empty folder.
fs.removeAll(path)nil · errorA folder and everything inside.
fs.glob(pattern)listPaths matching *, ?, **, sorted.
fs.cwd() · fs.tempDir()strWorking folder · system temp folder.

fogu:path

FunctionReturnsDescription
path.join(...parts)strJoined with / and normalized.
path.dirname(p) · path.basename(p)strFolder · last component.
path.stem(p) · path.extname(p)strName without extension · extension with the dot.
path.normalize(p)strResolves . and ...
path.isAbsolute(p)bool
path.absolute(p)strAbsolute path from the working folder.
path.sepstrThe system separator.

fogu:process

A program that exits with a non-zero code is not an error: check ok and code. Not being able to start it is an error value. For pipes and Linux commands see fogu:shell.

FunctionReturnsDescription
process.run(cmd, args?, { cwd, env, input })map · error{ ok, code, stdout, stderr }.
process.shell(command)mapThrough sh -c / cmd /C.
process.env(name) · process.setEnv(name, value)str · nil
process.cwd() · process.chdir(path)str · nil
process.pid · process.os · process.arch · process.args
process.exit(code)—

fogu:sync

Coordination of fg tasks. Waiting operations yield to the other tasks; waiting forever is reported as a deadlock.

FunctionReturnsDescription
sync.Mutex()Mutexlock() unlock() tryLock() → bool, field locked.
sync.WaitGroup()WaitGroupadd(n = 1) done() wait(), field count.
sync.Once()Oncedo(fn) runs fn only the first time.
sync.Atomic(value)Atomicget() set(v) add(n = 1) → new value, compareAndSwap(old, new) → bool.

fogu:test

Used in *.test.fg files, run with fogu test. A test fails when an expectation fails, when it raises an error or returns an error value.

FunctionReturnsDescription
test(name, fn)nilDeclares a test.
skip(name, fn)nilA test that is reported but not run.
expect(value)ExpectationThen a matcher; .not negates it.
toBe(x) · toEqual(x)Equality (toEqual compares lists and maps deeply).
toBeTruthy() · toBeFalsy() · toBeNil() · toBeError()
toBeType("int") · toContain(x) · toHaveLength(n)
toBeGreaterThan(n) · toBeLessThan(n) · toBeCloseTo(x, eps?)

fogu:log

Structured logs to stderr (default), stdout or a file. The level comes from FOGU_LOG (default info).

FunctionReturnsDescription
log.trace/debug/info/warn/error(message, fields?)nilfields: a map added as key=value (or JSON keys).
log.setLevel(level)niltrace debug info warn error off.
log.level()strCurrent level.
log.setFormat("text" | "json")nilOne line per entry.
log.setOutput("stderr" | "stdout" | path)nil · errorA file is appended to.
log.setTime(bool)nilShow the time stamp (default true).
log.child(fields)LoggerA logger that adds fields to every line; same methods + child.

fogu:regex

Rust regex syntax (linear time, no backtracking). Positions are character indexes. In Fogu strings write \\d for \d. An invalid pattern is an error value.

FunctionReturnsDescription
regex.compile(pattern, { ignoreCase, multiline, dotAll, verbose })Regex · error
re.test(text)boolMatches somewhere.
re.matches(text)boolThe whole text matches.
re.find(text)map · nil{ text, start, end, groups, named }.
re.findAll(text)listEvery match.
re.replace(text, repl) · re.replaceAll(text, repl)str$1, ${name}, $$ in repl.
re.split(text, limit?)list
re.source · re.groups · re.namesPattern · group count · group names.
regex.test/find/findAll/replace/replaceAll/split/matches(text, pattern, …)One-off shortcuts.
regex.escape(text)strA literal pattern.

fogu:crypto

FunctionReturnsDescription
crypto.hash(algo, data, { base64 })strmd5 sha1 sha256 sha384 sha512; hex by default.
crypto.hmac(algo, key, data, { base64 })str
crypto.encrypt(data, key)strAES-256-GCM; key is a password (argon2id) or 64 hex chars. Base64 output with salt and nonce.
crypto.decrypt(box, key, { bytes })str · errorWrong key or altered data → error value.
crypto.hashPassword(password)strargon2id PHC string, for storing passwords.
crypto.verifyPassword(password, hash)bool
crypto.randomBytes(n) · crypto.randomHex(n)list · strSecure random.
crypto.randomInt(min, max)intInclusive, unbiased.
crypto.uuid()strRandom UUID v4.
crypto.base64(data, { url }) · crypto.fromBase64(text, { bytes })strdata: str or list of bytes.
crypto.hex(data) · crypto.fromHex(text, { bytes })str
crypto.equal(a, b)boolConstant-time comparison.

fogu:csv

Rows with a header become maps; cells are typed (42 → int, 3.5 → float, true → bool, empty → nil; codes like 007 stay text). The delimiter is guessed (, ; tab |).

FunctionReturnsDescription
csv.parse(text, { header = true, delimiter, types = true, trim })list · errorMaps (or lists without header).
csv.stringify(rows, { header = true, delimiter = ",", columns })strRows: maps (a header line is written) or lists.
csv.read(path, options?) · csv.write(path, rows, options?)list · nilThe same, from/to a file.

fogu:excel

Reads .xlsx .xlsm .xls .xlsb .ods; writes .xlsx (bold, frozen header row, column widths fitted). Dates become ISO text.

FunctionReturnsDescription
excel.read(path, { sheet, header = true })list · map · errorThe first sheet (or sheet: name or index) as rows; sheet: "*" → { name: rows }.
excel.sheets(path)list · errorSheet names.
excel.write(path, rows | { Sheet: rows }, { header = true, columns })nil · errorOne sheet per map key.

fogu:yaml

FunctionReturnsDescription
yaml.parse(text)value · error
yaml.parseAll(text)list · errorEvery document of a --- stream.
yaml.stringify(value)str
yaml.read(path) · yaml.write(path, value)value · nil

fogu:toml

Dates become text. TOML has no null: nil values are left out when writing; the top level must be a map.

FunctionReturnsDescription
toml.parse(text)map · error
toml.stringify(map)str
toml.read(path) · toml.write(path, map)map · nil

fogu:xml

An element is { name, attrs, children, text }; children holds elements (maps) and text (str); text joins the element's own text.

FunctionReturnsDescription
xml.parse(text, { whitespace })map · errorThe root element.
xml.stringify(element, { pretty = true, declaration = true })str
xml.read(path) · xml.write(path, element, options?)map · nil

fogu:kv

An embedded key-value store in a single file (redb, ACID). Values are any Fogu value (stored as JSON).

FunctionReturnsDescription
kv.open(path)Store · errorCreates the file if needed.
store.set(key, value) · store.get(key)nil · valueget → nil when missing.
store.has(key) · store.delete(key)bool
store.keys() · store.len() · store.clear()list · int · nil
store.path · store.close()str · nil

fogu:sql

SQLite, PostgreSQL and MySQL/MariaDB by URL: sqlite://app.db, sqlite::memory:, postgres://user:pass@host/db, mysql://user:pass@host/db. Parameters: ? (SQLite/MySQL) or $1 (PostgreSQL).

FunctionReturnsDescription
sql.open(url)Database · error
db.run(sql, params?)int · errorOne statement with parameters; returns the affected rows (alias execute).
db.query(sql, params?)list · errorRows as maps.
db.one(sql, params?)map · nil · errorThe first row.
db.exec(sql)nil · errorRaw SQL without parameters — may hold several statements (migrations).
db.begin() · db.commit() · db.rollback()nil · errorTransactions.
db.driver · db.url · db.close()

fogu:orm

A Prisma-style ORM for SQLite, PostgreSQL and MySQL. Models are described in a map; migrate: "auto" creates tables (with foreign keys) and adds new columns. Each model is reached as db.<model in camelCase>. Relations load with one query per level.

Client and schema

FunctionReturnsDescription
create_client({ schema, migrate, log })Client · errorschema: { provider, url, models }; log: true prints every SQL statement.
field: { type, primary, autoincrement, required, unique, defaultValue }Types Int BigInt String Bool Float DateTime Json; defaults "now", "uuid" or a value.
relation: { model, kind, foreignKey, references }one-to-many, many-to-one, one-to-one.
db.query(sql, params?) · db.execute(sql, params?)list · intRaw SQL.
db.begin() · db.commit() · db.rollback() · db.migrate() · db.disconnect()

Model methods

where: { field: value }, { field: { equals, not, in, notIn, lt, lte, gt, gte, contains, startsWith, endsWith, mode: "insensitive" } }, AND/OR/NOT, and nil for IS NULL. orderBy: { field: "asc" | "desc" } or a list of them.

FunctionReturnsDescription
create({ data, include, select })mapMissing required fields stop the program; a duplicate is an error value.
createMany({ data: [...] }){ count }
findMany({ where, select, include, orderBy, take, skip })list
findFirst(opts) · findUnique(opts)map · nil
update({ where, data })map · errordata values may be { increment, decrement, multiply, divide, set }.
updateMany({ where, data }) · deleteMany({ where }){ count }
upsert({ where, create, update })map
delete({ where })map · errorThe deleted record.
count({ where })int

fogu:redis

Redis, Valkey and KeyDB. Every Redis command is a method (r.lpush(…), r.xadd(…)) or goes through r.command("CMD", …); maps become field value pairs. Redis errors (WRONGTYPE, NOAUTH) are error values.

FunctionReturnsDescription
redis.connect(url | { host, port, username, password, db, tls, timeout, name })Redis · errorredis://[user:pass@]host[:port][/db], rediss:// for TLS, default $REDIS_URL.
r.set(key, value, { ex, px, exat, nx, xx, keepTtl, get })bool · valuefalse when nx/xx prevented it.
r.get(key) · r.del(keys) · r.incr(key) · r.expire(key, s) · r.ttl(key)
r.hset(key, map) · r.hget(key, f) · r.hgetall(key)int · str · map
r.zrange(key, start, stop, { withScores, rev, byScore })listWith scores: [{ member, score }].
r.setJson(key, value, opts?) · r.getJson(key)bool · valueJSON values.
r.pipeline([[cmd, …args], …])listOne round trip; a failed command is an error value in its place.
r.transaction([...])list · errorMULTI … EXEC.
r.subscribe(...channels) · r.psubscribe(...patterns)SubscriptionIts own connection: next(timeout?) → { channel, message, pattern? }, tryNext(), close().
r.publish(channel, message)intReceivers.
r.close()nil

fogu:s3

AWS S3 and compatible storage (MinIO, Cloudflare R2, Backblaze B2, DigitalOcean Spaces, Wasabi) with AWS Signature V4. Credentials default to $AWS_ACCESS_KEY_ID, $AWS_SECRET_ACCESS_KEY, $AWS_REGION, $AWS_ENDPOINT_URL.

Every method also takes { bucket } in its options.

FunctionReturnsDescription
s3.connect({ endpoint, region, accessKey, secretKey, sessionToken, bucket, pathStyle, timeout })S3Path-style URLs when an endpoint is given.
store.put(key, body, { contentType, metadata, cacheControl, acl }){ etag } · errorbody: str, list of bytes, or any value (JSON).
store.upload(path, key?, opts?){ etag } · errorA local file.
store.get(key) · store.getBytes(key) · store.download(key, path)str · list · nil
store.head(key) · store.exists(key)map · nil · bool{ size, etag, contentType, lastModified, metadata }.
store.list(prefix?, { delimiter, max })list{ key, size, etag, lastModified }; folders { key, dir: true }.
store.copy(from, to, { fromBucket }) · store.delete(key | keys)
store.presign(key, { method = "GET", expires = 1h })strA temporary URL that needs no credentials.
store.url(key) · store.withBucket(name)str · S3
store.createBucket(name?) · store.deleteBucket(name?) · store.listBuckets()

fogu:scylla

ScyllaDB and Apache Cassandra (CQL). Statements with parameters are prepared once and cached; parameters are converted to the column types (collections, UDTs, uuid, timestamp…).

FunctionReturnsDescription
scylla.connect("host:port" | { hosts, keyspace, user, password, singleNode, addressMap })Scylla · erroraddressMap translates addresses of nodes behind Docker/NAT.
db.execute(cql, params?)nil · error
db.query(cql, params?)list · errorRows as maps.
db.one(cql, params?)map · nil · error
db.use(keyspace) · db.close()

fogu:http

A minimal HTTP server (one handler for every request) and an HTTP client. For routing, middleware, static files, uploads, SSE and WebSocket use fogu:web.

Client

FunctionReturnsDescription
http.get(url) · http.delete(url)Response · errorNetwork failures are error values; HTTP 4xx/5xx are normal responses.
http.post(url, body) · http.put(url, body)Response · errorbody: str, or a map sent as JSON.
res.status · res.headers · res.body · res.json()

Server

FunctionReturnsDescription
http.serve(port, fn(req))nilRequests are handled while the program runs (e.g. in a while loop with time.delay).
req.method · req.path · req.query · req.headers · req.bodyquery is the raw query string.
req.header(name) · req.json() · req.form() · req.bytes()form(): urlencoded and multipart bodies.
http.text(body, opts?) · http.json(value, opts?) · http.html(body, opts?)Responseopts: { status, headers }.

fogu:web

A web framework: routes with parameters, middleware, static files and SPA builds, uploads, downloads, Server-Sent Events and WebSocket — on one port.

App

FunctionReturnsDescription
web.app()AppMethods return the app, so calls can be chained.
app.get/post/put/patch/delete/head/options/all(path, ...middlewares, handler)App/users/:id parameters, * wildcard.
app.use(fn) · app.use(prefix, fn)AppMiddleware for every request, or under a path prefix.
app.after((ctx, res) => …)AppAfter the handler; returning a value replaces the response.
app.onError((ctx, err) => response)AppFor runtime errors and returned error values (default: 500).
app.static(dir) · app.static(prefix, dir, { spa, index, maxAge, dotfiles })AppFiles served by the server thread: ETag/304, Cache-Control; spa: true serves index.html for unknown HTML routes (React, Vue, Svelte builds).
app.ws(path, ...middlewares, (ws, ctx) => …, { protocols })AppWebSocket routes; the handler runs in its own task with a WebSocket.
app.set(key, value) · app.storeApp · mapShared state (ctx.store).
app.listen(port, { host = "127.0.0.1", maxBody = 10 MB, quiet })nil · errorUse host: "0.0.0.0" to accept other machines.
app.handler()fnCalls the app without a network (tests): h({ method, path, headers, body }) → Response.
app.routes()list

Context and responses

FunctionReturnsDescription
ctx.method · ctx.path · ctx.params · ctx.query · ctx.headers · ctx.bodyquery is a map; header names are lower case.
ctx.req.json() · ctx.req.form() · ctx.req.bytes()form(): fields as str, files as UploadedFile.
ctx.set.status · ctx.set.headersAdjust the final response from middleware or handlers.
ctx.storemapThe app's shared map.
handler resultA str (text or HTML), any value (JSON), a response, or nil (204).
web.json/text/html(body, { status, headers })Response
web.redirect(url, status = 302)Response
web.file(path, { download, contentType, status })Response · errorSent by the server thread (binary safe).
web.sse({ retry, headers })SseStreamReturn it from the handler; send(data, { event, id }), comment(text), close(), field open.
web.cors({ origin, methods, headers, credentials, maxAge })middlewareAnswers preflight requests.
web.logger()middlewareGET /path 200 0.4ms.
UploadedFile: filename, contentType, size, text(), bytes(), json(), save(path)save("uploads/") keeps a sanitized file name.

Without a network, through app.handler():

fogu:jwt

JSON Web Tokens with HMAC (HS256/HS384/HS512). A token that fails verification is an error value.

FunctionReturnsDescription
jwt.sign(payload, secret, { algorithm, expiresIn, notBefore, issuer, audience, subject, jwtid, header, noTimestamp })striat is added; durations or seconds for times.
jwt.verify(token, secret, { algorithms, issuer, audience, subject, leeway, maxAge, ignoreExpiration })map · errorThe claims.
jwt.decode(token)map · error{ header, payload } without verifying.
jwt.middleware(secret, { property = "user", optional, …verify options })middlewareFor fogu:web: Authorization: Bearer → ctx.user, otherwise 401.

fogu:websocket

WebSocket client (ws://, wss://) and a standalone server. The same connection object is used by app.ws in fogu:web.

FunctionReturnsDescription
websocket.connect(url, { headers, protocols, timeout })WebSocket · errorA rejected handshake (401, 404…) is an error value.
websocket.listen(port, { host })Server · errorport 0 picks a free port; server.accept(timeout?) → WebSocket, fields port url.
ws.send(data)nil · errorstr → text frame, list of bytes → binary, other values → JSON text.
ws.next(timeout?)Message · nil{ type: "text" | "binary", data, bytes, size, json() }; nil on timeout or when closed.
ws.tryNext() · ws.ping() · ws.close(code = 1000, reason)
ws.open · ws.closeCode · ws.closeReason · ws.path · ws.url · ws.headers · ws.protocol

fogu:net

TCP and UDP sockets. Data is text (UTF-8).

FunctionReturnsDescription
net.tcpListen("host:port")Listener · erroraccept() → Stream, setNonblocking(bool), close().
net.tcpConnect("host:port")Stream · error
stream.write(text) · stream.writeLine(text)nil · error
stream.read(n = 4096) · stream.readLine() · stream.readAll()str · errorreadLine without the newline; "" at the end.
stream.setTimeout(ms) · stream.close()
net.udpBind("host:port")Socket · errorsendTo(addr, data), recvFrom() → { data, addr, len }, connect(addr), send(data), recv(), setTimeout(ms).
net.resolve(host)list · errorIP addresses.

fogu:events

FunctionReturnsDescription
events.EventEmitter()Emitter
e.on(event, fn) · e.once(event, fn)nilListeners receive every emitted argument (declare them all).
e.off(event, fn?) · e.removeAll(event?)nil
e.emit(event, ...args)booltrue when someone listened.
e.listeners(event) · e.eventNames() · e.hasListeners(event)

fogu:cron

Jobs run when the program calls cron.tick() (typically in its main loop).

FunctionReturnsDescription
cron.every(interval, fn)nilA job every interval (duration or ms).
cron.schedule(expression, fn)nil5-field cron expression ("*/5 * * * *"); an invalid one stops the program.
cron.tick()intRuns the jobs that are due; returns how many ran.
cron.list()listJob names (every(10ms), the cron expression…).
cron.clear()nilRemoves every job.

fogu:nats

NATS core protocol plus JetStream (streams, acknowledged publishing, pull consumers).

FunctionReturnsDescription
nats.connect(url | { url, user, pass, token, name, timeout })Nats · errornats://[user:pass@|token@]host:4222, default $NATS_URL.
nc.publish(subject, data?, { headers, reply })nil · errordata: str, or any value as JSON.
nc.subscribe(subject, { queue, max })SubscriptionWildcards * and >; queue groups split the messages.
sub.next(timeout?) · sub.tryNext() · sub.unsubscribe()Message · nil
nc.request(subject, data?, { timeout, headers })Message · errorNo responders → error value.
nc.flush() · nc.close() · nc.connected · nc.server
msg.subject · .data · .headers · .reply · .json() · .respond(data)

JetStream

FunctionReturnsDescription
nc.jetstream({ prefix, timeout })JetStream
js.addStream({ name, subjects, storage, max_msgs, max_age, … })map · errorNATS stream config (snake_case keys).
js.updateStream(cfg) · js.streamInfo(name) · js.deleteStream(name) · js.purgeStream(name)
js.publish(subject, data, { msgId, headers, timeout }){ stream, seq, duplicate }msgId deduplicates.
js.consumer(stream, { durable, filter, ackWait, deliver, maxDeliver })Consumer · errorA durable pull consumer.
consumer.fetch(batch = 1, expires = 5s)listUp to batch messages.
msg.ack() · msg.nak(delay?) · msg.term() · msg.inProgress()nilmsg.seq, msg.delivered, msg.stream.

fogu:rabbitmq

RabbitMQ (AMQP 0-9-1). Publishing waits for the broker's confirmation. A channel closed by an error (for example declaring a queue with other options) is reopened on the next call.

FunctionReturnsDescription
rabbitmq.connect(url?)RabbitMQ · erroramqp://user:pass@host:5672/vhost, default $AMQP_URL.
mq.exchange(name, kind = "direct", { durable, autoDelete, internal, arguments })nil · errordirect fanout topic headers.
mq.queue(name = "", { durable, exclusive, autoDelete, ttl, maxLength, deadLetter, passive }){ name, messages, consumers }"" gives a server-named queue.
mq.bind(queue, exchange, key = "") · mq.unbind(…)nil · error
mq.publish(exchange, key, data, { persistent, headers, contentType, correlationId, replyTo, messageId, expiration, priority, mandatory })nil · errorMaps are sent as JSON.
mq.send(queue, data, opts?)nil · errorThrough the default exchange.
mq.get(queue, { noAck })Message · nil
mq.consume(queue, { prefetch = 10, noAck, exclusive, tag })Consumernext(timeout?), tryNext(), cancel().
msg.data · .json() · .routingKey · .exchange · .headers · .redelivered · …Properties: contentType correlationId replyTo messageId type persistent.
msg.ack() · msg.nack({ requeue = true }) · msg.reject({ requeue })nil
mq.purge(queue) · mq.deleteQueue(name) · mq.deleteExchange(name) · mq.close()

fogu:mqtt

MQTT 3.1.1 with automatic reconnection (subscriptions are restored).

FunctionReturnsDescription
mqtt.connect(url?, { clientId, username, password, keepAlive, clean = true, will, timeout })Mqtt · errormqtt:// or mqtts://; will: { topic, payload, qos, retain }.
m.publish(topic, data, { qos = 0, retain })nil · error
m.subscribe(filter | [filters], { qos }) · m.unsubscribe(…)nil · error+ one level, # the rest.
m.next(timeout?) · m.tryNext()Message · nil{ topic, data, qos, retain, json(), matches(filter) }.
m.connected · m.clientId · m.subscriptions · m.close()
mqtt.matches(filter, topic)bool

fogu:fogumq

Client of the Fogu broker (binary protocol, JSON payloads): key/value store, channels, pub/sub, RPC and API keys. Methods that wait for the server let the other fg tasks run.

FunctionReturnsDescription
fogumq.connect("fogu://token@host:port", { timeout, requestTimeout })FoguMQ · errorFields connected authenticated isRoot url; ping() close().
mq.kv.get(key) · set(key, value, ttl?) · del(key) · exists(key) · incr(key, n?) · decr(key, n?)value · errorget gives nil for a missing key.
mq.channel(name)Channeljoin(alias?) leave() send(data) members() list().
channel.onMessage()Listenernext(timeout?) → { channel, data, ... }; tryNext() close().
channel.subscribe(topic) · channel.publish(topic, data)Subscription · errorTopics live under the channel: name/topic.
channel.act(method)Service · errornext() → Request with method params reply(result) fail(msg).
channel.call(method, params?, timeout?)value · error
mq.apikeys.create(name, { channels, kv, topics, rpc }) · delete(id) · get(id) · list()value · errorRoot token only.

fogu:bullmq

Job queues on Redis, wire-compatible with BullMQ for Node.js v5 (on the bullmq-rs crate): Fogu and Node producers and workers can share the same queues, and dashboards such as Bull Board see them. The worker owns locks, stalled-job recovery, retries with backoff, concurrency and rate limits; each job is handed to the program, which finishes it with complete or fail. Redis failures are error values.

Queue

FunctionReturnsDescription
bullmq.queue(name, { connection, prefix = "bull" })BullQueue · errorconnection is a redis:// / rediss:// URL (default $REDIS_URL or redis://127.0.0.1:6379).
q.add(name, data, opts?)job · errorOptions: delay attempts backoff: { type: "fixed" | "exponential", delay, max } priority jobId ttl deduplication: { id, ttl }.
q.addBulk([{ name, data, opts }])list · error
q.getJob(id) · q.getJobs(states, start = 0, end = -1, asc = false)job | nil · listA job: { id name data state timestamp attemptsMade progress processedOn finishedOn failedReason returnvalue opts }.
q.counts() · q.count()map · intPer state: wait active delayed prioritized completed failed paused waiting-children.
q.upsertJobScheduler(id, { every | pattern, tz, limit, immediately }, { name, data, opts })job · errorRepeatable jobs (cron pattern or every duration).
q.removeJobScheduler(id)bool · error
q.pause() · resume() · isPaused() · drain() · remove(id) · clean(grace, limit, state) · obliterate({ force }) · getJobLogs(id)

Worker and events

Limitation: the worker of bullmq-rs publishes completed events with an empty return value; q.getJob(id).returnvalue has the value given to complete.

FunctionReturnsDescription
bullmq.worker(name, { connection, prefix, concurrency = 1, lockDuration, maxStalledCount, limiter: { max, duration } })BullWorker · error
w.next(timeout?) · w.tryNext()BullJob | nil · errorWaits cooperatively (other fg tasks keep running); nil on timeout or after close().
job.complete(value?) · job.fail(reason)nilfail retries the job while attempts remain. A job dropped without an answer fails.
job.progress(value) · job.log(text)nilFields are those of the job map, plus done.
w.pause() · w.resume() · w.close() · w.running · w.pending
bullmq.events(name, { connection, prefix, lastEventId })BullEvents · errornext(timeout?) → { event, jobId, id, … } (added waiting active progress completed failed delayed stalled removed).

fogu:mail

E-mail over SMTP (presets for Gmail, Outlook, Office 365, Yahoo, iCloud, Zoho, Fastmail, AOL, GMX, Amazon SES, Mailtrap, Mailpit) or the HTTP APIs of Resend, SendGrid, Mailgun, Postmark, Brevo, MailerSend and Mailjet. The console provider only prints (development and tests).

FunctionReturnsDescription
mail.connect({ provider, … } | url)MailerNo connection is made yet.
SMTP: { provider: "gmail" | "smtp" | …, host, port, secure, user, password, region }secure: tls (465), starttls (587), none; URLs smtp://user:pass@host:587, smtps://….
APIs: { provider: "resend" | "sendgrid" | …, apiKey, domain, region, baseUrl }apiKey defaults to $RESEND_API_KEY, $SENDGRID_API_KEY…; Mailgun needs domain, Mailjet apiSecret.
common: { from, timeout }Default sender.
mailer.send(message){ id, provider } · errorA rejected message, a bad address or a network failure is an error value.
mailer.sendMany([messages])list · error
mailer.verify()bool · errorSMTP: connects and authenticates.
mailer.render(message)strThe RFC 5322 text that SMTP would send.
message: { from, to, cc, bcc, replyTo, subject, text, html, attachments, headers }Addresses: "a@x.com", "Ana <a@x.com>", { name, email } or a list.
attachment: { filename, content | path, contentType, cid, encoding }content: str or list of bytes; cid makes it inline (<img src="cid:logo">).

fogu:ai

LLMs over HTTP: OpenAI-compatible APIs (OpenAI, Groq, OpenRouter, DeepSeek, Mistral, xAI, Together, Fireworks, Perplexity, Ollama, LM Studio), Anthropic and Google Gemini — chat, streaming, tools, agents, embeddings. Keys default to $OPENAI_API_KEY, $ANTHROPIC_API_KEY, $GEMINI_API_KEY…

Client

FunctionReturnsDescription
ai.client({ provider, model, apiKey, baseUrl, timeout, headers, maxTokens } | "provider/model")AiClientProvider compatible + baseUrl for any OpenAI-style server.
client.chat(prompt | { messages, prompt, system, model, temperature, maxTokens, topP, stop, tools, toolChoice, json })map · error{ text, toolCalls, finishReason, usage: { inputTokens, outputTokens }, model, message }.
client.stream(same)AiStream · errornext(timeout?) → the next piece of text or nil at the end; collect() → like chat; fields text done result.
client.models()list · error[{ id, name }].
client.embed(text | [texts], { model })list · errorVectors (OpenAI-style and Gemini).
message: { role: "system" | "user" | "assistant" | "tool", content, toolCalls, toolCallId, name }result.message can be appended to continue the conversation.

Tools and agents

A tool that returns an error value sends error: … back to the model, which can recover.

FunctionReturnsDescription
ai.tool(name, description, parameters, run?)mapparameters is a JSON schema; also a plain map { name, description, parameters, run }.
ai.agent({ client, model, instructions, tools, maxSteps = 10, temperature, maxTokens, memory, onStep })AgentTools need run(args).
agent.run(input)map · errorCalls the model and the tools until it answers: { text, steps, messages, usage, finishReason }.
agent.reset() · agent.historyWith memory: true the conversation continues across runs.

fogu:ssh

Commands, file copies and tunnels over the system's OpenSSH client (key or password).

FunctionReturnsDescription
ssh.connect("user@host:port" | { host, user, port, key, password, strictHostKey, knownHosts, timeout, options })Ssh · errorChecks the connection; strictHostKey defaults to accept-new.
s.run(command, { input }){ ok, code, stdout, stderr } · errorA non-zero exit code is not an error.
s.upload(local, remote) · s.download(remote, local)nil · errorscp, folders included.
s.forward(localPort | 0, host, port)Tunnel · error-L: a local port that reaches host:port through the server.
s.reverse(remotePort, host, localPort)Tunnel · error-R: a port on the server that reaches a local service.
tunnel.localPort · .remotePort · .alive() · .close()
s.close()nil

fogu:shell

A small POSIX-style shell: pipes, && || ;, redirections (> >> < 2> 2>&1 &>), $VAR, $(…), quotes, globs and ~. The usual Linux commands are built in, so scripts behave the same on Windows: echo cat ls pwd cd mkdir rm cp mv touch grep head tail wc sort uniq cut tr tee basename dirname which env export true false exit sleep seq. Other commands run as programs.

FunctionReturnsDescription
shell.run(command, { cwd, env, input, native = true }){ ok, code, stdout, stderr }A non-zero exit is not an error; native: false uses system programs only.
shell.exec(command, opts?)sameOutput goes to the terminal as it happens.
shell.text(command, opts?)str · errorstdout without the final newline; a failure is an error value.
shell.lines(command, opts?) · shell.json(command, opts?)list · value
shell.quote(text)strSafe to interpolate into a command.
shell.cd(dir) · shell.cwd()nil · strThe shell's own working folder.
shell.which(program) · shell.builtinsstr · list

fogu:ffi

Calls functions of native libraries (C ABI): .dll, .so, .dylib. Unsafe by nature: a wrong signature can crash the program. Supported signatures: up to 4 arguments mixing integers and floats, or up to 8 integer/pointer arguments.

FunctionReturnsDescription
ffi.open(name)Library · errorA path or a name ("user32" → user32.dll / libuser32.so); "c" and "m" are the C runtime and the math library.
lib.func(symbol, [argTypes], returnType = "void")fn · errorA Fogu function that calls the symbol.
lib.has(symbol) · lib.namebool · str
typesi8 i16 i32 i64 u8 u16 u32 u64 isize usize bool f64 ptr str buffer; results also f32 void. str passes a NUL-terminated copy; a str result reads a C string.
ffi.alloc(size)BufferMemory for out-parameters: text() bytes(start?, len?) write(offset, str | bytes) i32/u32/i64/f64(offset), fields size address.
ffi.types · ffi.suffixlist · str

fogu:playbook

Configuration management in the style of Ansible: an inventory of hosts and playbooks of idempotent tasks, run over SSH (the system's OpenSSH client) or on the local machine (through fogu:shell, so it also works on Windows). Hosts run in parallel; the output and the PLAY RECAP follow Ansible's. From the command line: fogu play site.yml -i inventory.yml [--check] [-l hosts] [-t tags] [--skip-tags t] [-e key=value] [-f forks] [-v] (exit code 2 when a host failed or was unreachable).

Inventory

vars for every host; hosts: name → { host, port, user, key, password, become_password, connection: "local", …vars }; groups: name → list of hosts or { hosts, vars }. Patterns in hosts: — all, a host, a group, web:db, web:!web2.

Playbooks and tasks

Templates: {{ expr }} with variables (item, facts, inventory_hostname, groups, registered results), . [], + - * / % ~, comparisons, and or not in, is defined, and the filters default upper lower trim length join first last replace split int float string bool to_json basename dirname.

FunctionReturnsDescription
play: { name, hosts, vars, become, gather_facts = true, tags, tasks, handlers }Facts: facts.os arch hostname distribution distribution_version pkg_mgr service_mgr user.
task keys: name, when, loop, register, notify, ignore_errors, changed_when, check_mode, become, tagswhen is an expression; loop sets item; register keeps the result.
shell / command: "cmd" | { cmd, chdir, creates, removes, stdin }Skipped in check mode (unless check_mode: false); { rc, stdout, stderr, stdout_lines }.
copy: { content | src, dest, mode } · template: { src, dest, mode }Written only when the content or mode differs; template renders {{ }}.
file: { path, state: directory | absent | touch | file, mode }
lineinfile: { path, line, regexp, state, create, mode }Replaces the last line matching regexp, or appends.
package: { name, state: present | absent, update_cache, use }apt, dnf, yum, zypper, apk, pacman (from the facts).
service: { name, state: started | stopped | restarted | reloaded, enabled }systemd.
debug: { msg } | { var } · set_fact: { … } · fail: { msg } · wait_for: { port | path, host, timeout }

From Fogu

FunctionReturnsDescription
playbook.run(plays | path, { inventory, vars, check, limit, tags, skipTags, forks, verbose, quiet })map · error{ ok, failed: [hosts], recap: { host: { ok, changed, unreachable, failed, skipped, ignored } } }.
playbook.inventory(path | map)mapThe hosts and groups it describes.
playbook.render(text, vars)str · errorThe template engine on its own.

fogu:vm

Runs Fogu source code at run time, in the style of Node's node:vm. The code runs on the same interpreter, so its functions can be called from the program and vice versa. A new context only sees the global natives plus the keys of its sandbox map (it may still import modules); like node:vm, it is not a security boundary. The value of the last expression is returned; syntax, name, runtime and timeout errors come back as error values.

FunctionReturnsDescription
vm.runInNewContext(code, sandbox?, options?)value · errorOptions: { filename = "<vm>", timeout } or a filename. Variables the code declares are copied into the sandbox map.
vm.createContext(sandbox?) · vm.isContext(x)VmContext · boolA context keeps its variables between runs: ctx.sandbox, ctx.name reads a variable, ctx.run(code, options?).
vm.runInContext(code, ctx, options?)value · errorChanges to the sandbox map are seen by the next run.
vm.runInThisContext(code, options?)value · errorSees the program's globals; its declarations persist between calls.
vm.Script(code, options?)VmScript · errorParsed once: runInContext(ctx) runInNewContext(sandbox?) runInThisContext(), fields source filename.
vm.compileFunction(code, params = [], { context, filename })fn · errorA function whose body is code.
timeoutA duration; loops and calls stop once it passes (script execution timed out after 50ms). The JIT is not used while it runs.

fogu:cli

Terminal interaction: prompts (dialoguer), colors and terminal control (console), spinners and progress bars (indicatif), tables (comfy-table) and argument parsing (clap). Without a terminal on stdin (pipes, CI) the prompts read plain lines, so echo Ana | fogu app.fg works; spinners and bars draw on stderr, only on a terminal. Colors are off when stdout is not a terminal or NO_COLOR is set.

Prompts

FunctionReturnsDescription
cli.input(prompt, { default, allowEmpty, type: "str" | "int" | "float" })str | int | float · error
cli.password(prompt, { confirm: "Repeat password", allowEmpty })str · errorHidden input.
cli.confirm(prompt, { default })bool · error
cli.select(prompt, items, { default, fuzzy, index })value | nil · errorItems are strings or { label, value }; nil when cancelled with Esc; fuzzy filters while typing.
cli.multiSelect(prompt, items, { defaults, index })list | nil · errorWithout a terminal: names or numbers separated by commas.

Styles, terminal, spinners and tables

FunctionReturnsDescription
cli.style(text, ...styles)strbold dim italic underline strike inverse, colors red green yellow blue magenta cyan white black gray, brightRed, backgrounds bgRed, 256 colors "208" / "bg208".
cli.red(text) · green · yellow · blue · magenta · cyan · white · black · gray · bold · dim · italic · underline · strike · inversestr
cli.colors(on?) · cli.stripAnsi(s) · cli.textWidth(s) · cli.link(text, url)bool · str · int · strcolors(true) forces colors.
cli.isTTY("stdin" | "stdout" | "stderr") · cli.size()bool · { columns, rows } | nil
cli.clear() · clearLine() · hideCursor() · showCursor() · readKey()readKey → a character or Enter Escape Backspace Tab ArrowUp ArrowDown ArrowLeft ArrowRight Home End PageUp PageDown Delete.
cli.spinner(text, { frames })Spinnertext(t) succeed(msg?) fail(msg?) warn(msg?) info(msg?) stop().
cli.progress(total, { message, prefix, template, chars })ProgressBarinc(n = 1) set(n) length(n) message(m) println(line) finish(msg?), fields position length elapsed finished. Templates of indicatif.
cli.table(rows, { headers, style: "rounded" | "utf8" | "ascii" | "markdown" | "none", align, width })strRows are lists or maps; numbers are right aligned.

Arguments

FunctionReturnsDescription
cli.parse(spec, argv = Fogu.args, { exit = true })map · errorspec: { name, version, about, args: [...], commands: [{ name, about, args }], commandRequired }. --help and --version print and exit; a usage error prints and exits with 2, or is an error value with { exit: false }.
arg: { name, type, short, long, help, positional, required, multiple, default, choices, env }Types str int float bool count; multiple gives a list. A subcommand sets command and its values in args.

Command line

fogu fmt indents with 4 spaces, normalizes the spacing around commas, colons, operators and braces, keeps one blank line at most and never touches strings or comments. It checks that the formatted program has exactly the same tokens before writing a file; --check lists the files that need formatting (exit 1).

Option / variableEffect
--no-jit · FOGU_JIT=0interpreter only
--jit-stats · --jit-debug · --jit-dumpstatistics, what compiles/is rejected, Cranelift IR
--strictan error in an fg task terminates the process
FOGU_HOMEbase of the package store (default: ~/.fogu)
NO_COLORdisables colors
FOGU_DOTENV=0do not load ./.env into Fogu.env/process.env (loaded by default; variables already set win)

fogu init creates fogu.json (with start and dev scripts), main.fg and a .gitignore.

Diagnostics

Undefined names, uses before declaration, unknown types, misplaced ? and non-exhaustive match are detected before the program runs. Errors show a code, the position, an excerpt and, when possible, a suggestion. fogu check also warns about unused local variables (prefix them with _ to silence the warning).

CodeMeaning
E0001 · E0002lexical error · syntax error
E0100 · E0101undefined variable · used before its declaration
E0102 · E0103 · E0104redeclaration in the same scope · assignment to a const · private name
E0200 · E0201 · E0202integer overflow · division by zero · index out of bounds
E0203 · E0204 · E0205non-exhaustive match · value is not callable · stack overflow
E0206? outside a function, or in a function whose return type has no error
E0207 · E0208type mismatch · unknown type
E0300deadlock between tasks

Testing

Tests live in *.test.fg files and use fogu:test. fogu test finds them (under the current folder, or the given files and folders), runs each file in a fresh interpreter and exits with 1 if a test fails. A test fails when an expectation fails, when it raises an error, or when it returns an error value — so ? can be used inside tests.

Matchers: toBe, toEqual, toBeTruthy, toBeFalsy, toBeNil, toBeError, toBeType("int"), toContain, toHaveLength, toBeGreaterThan, toBeLessThan, toBeCloseTo(x, eps?) — each can be negated with .not.

Packages

Packages come from git repositories, .tar.gz archives or local folders and live in a global store (~/.fogu/fogu_modules/<name>@<commit>). fogu.lock pins the commit and the sha256 checksum; install scripts are never run.

Standalone executables

fogu build --compile produces dist/<name> (.exe on Windows): a copy of the Fogu runtime with the program appended — the entry file, every module it imports and the packages it uses. The executable runs on machines without Fogu and passes every command-line argument to the program.

Files the program reads at runtime (a React build, templates, images…) are embedded with "embed": ["dist", "templates/*.html"] in fogu.json or --embed <glob>. The program finds them with Fogu.asset(path), which works the same when running from the sources:

On first run the bundle is extracted to <temp>/fogu-bundles/<hash>; --target wasm is not supported yet.

JIT compiler

The interpreter is the semantic reference. The JIT (Cranelift) compiles hot numeric functions and loops (int/float/bool, if, while, for a..b, match, math.*, numeric type annotations); when something is not supported, the code keeps running in the interpreter, with the same results and error messages.

Benchmark (release)InterpreterJIT
fib(32)3.02 s0.025 s
50M loop16.7 s0.069 s
mandelbrot3.5 s0.013 s

Status and roadmap

The language is in development (before 1.0 there may be incompatible changes). It does not have yet: permissions --allow-*, fogu audit, fogu publish, static type inference, or the bytecode VM. The detailed status of every feature is in CHECKLIST.md and the full specification in docs/spec.md.

The standard library reference on this page is generated by docs/tools/gen_reference.py, which runs the examples to show their real output.

Fogu is distributed under the MIT license © 2026 Edmilson Soares.