SSH & WebAssembly

SSH

SSH executes commands on remote machines directly over SSH. No agent required — connects with password or key authentication. UPLOAD and DOWNLOAD transfer files between your namespace and the remote machine.

Single command — password auth

Example
SSH "root" AT "123.45.67.89" PASSWORD "mypassword" ENTER `ls /var/www` SET ?result
AFTER EMIT ?result("stdout")

Key-based authentication

Example
SSH "deploy" AT "123.45.67.89" KEY "/root/keys/id_rsa" ENTER `git pull` SET ?result
AFTER EMIT ?result("stdout")

Custom port

Example
SSH "admin" AT "123.45.67.89" ON "2222" PASSWORD "mypassword" ENTER `uptime` SET ?result
AFTER EMIT ?result("stdout")

Multi-command block

Multiple commands are written as newlines inside the backtick block. They run sequentially in the same session.

Example
SSH "deploy" AT "123.45.67.89" KEY "/root/keys/id_rsa" ENTER `
cd /var/www/myapp
git pull
npm install --production
pm2 restart all
` SET ?result
AFTER EMIT ?result("stdout")

File transfer — namespace to remote

Example
SSH "deploy" AT "123.45.67.89" KEY "/root/keys/id_rsa" UPLOAD "/root/config.json" TO "/var/www/myapp/config.json"

File transfer — remote to namespace

Example
SSH "root" AT "123.45.67.89" PASSWORD "mypassword" DOWNLOAD "/var/log/app.log" INTO "/mounted/logs/app.log" SET ?path
AFTER EMIT ?path

Checking exit code

Example
SSH "root" AT "123.45.67.89" PASSWORD "mypassword" ENTER `systemctl is-active nginx` SET ?r
AFTER IF ?r("exit_code") IS EQUAL TO 0
OPEN
  EMIT "nginx is running"
CLOSE
OR
OPEN
  EMIT "nginx is down"
CLOSE

Return value

SSH returns an object for every ENTER call.

Field Type Description
stdoutstringStandard output from the command
stderrstringStandard error from the command
exit_codenumberProcess exit code — 0 is success
Verb Description
SSH "user" AT "host" PASSWORD "pass" ENTER `cmd` SET ?rSingle command via password auth. Default port 22.
SSH "user" AT "host" KEY "/root/key" ENTER `cmd` SET ?rSingle command via key auth.
SSH "user" AT "host" ON "port" [AUTH] ENTER `cmd` SET ?rCustom port.
SSH "user" AT "host" [AUTH] UPLOAD "/root/src" TO "/remote/dst"Copy file from namespace to remote machine.
SSH "user" AT "host" [AUTH] DOWNLOAD "/remote/src" INTO "/mounted/dst" SET ?pathCopy file from remote machine into namespace.

WebAssembly

The WEBASSEMBLY verb runs code in another language or a compiled binary, fully sandboxed from the rest of the server. Only your namespace’s /root and /mounted are visible inside the sandbox — nothing else on the server exists from its perspective.

GRANT

Before entering a sandbox, you choose exactly which OcaltQL variables it can see. WEBASSEMBLY GRANT exposes a variable under an alias. Nothing is shared unless explicitly granted.

Example
STRING "hello" SET ?var
AFTER WEBASSEMBLY GRANT ?var AS "varalias"

Multiple grants can be chained before a single ENTER block — each one adds another variable to the sandbox’s oql object.

Multiple Grants
STRING "Ocalt" SET ?name
AFTER NUMBER 5 SET ?count
AFTER WEBASSEMBLY GRANT ?name AS "name"
AFTER WEBASSEMBLY GRANT ?count AS "count"

TYPE & ENTER

WEBASSEMBLY TYPE "language" ENTER \`code\` SET ?result runs the code in the specified language, inside the sandbox, with access to anything granted beforehand. Whatever the code outputs becomes the result.

Example
STRING "hello" SET ?var
AFTER WEBASSEMBLY GRANT ?var AS "varalias"
AFTER WEBASSEMBLY TYPE "php" ENTER `
echo $oql['varalias'];
` SET ?result
AFTER EMIT ?result

Supported Types

Granted variables are available inside the sandbox as an oql object, in the idiomatic form for each language.

TYPE Access grants as
php$oql['alias']
pythonoql['alias']
nodeoql.alias
rubyoql['alias']
perl$oql{'alias'}
javaoql.get("alias")
bash$OQL_alias (env var)
ruststd::env::var("OQL_alias") (env var)
goos.Getenv("OQL_alias") (env var)
wasmpassed as WASI env vars OQL_alias

Compiled WebAssembly

When TYPE is wasm, ENTER takes a namespace path to a compiled .wasm file instead of inline source.

Example
WEBASSEMBLY TYPE "wasm" ENTER "/root/modules/compute.wasm" SET ?result
AFTER EMIT ?result

Sandbox isolation

Every WEBASSEMBLY execution runs in an isolated filesystem view. The code being executed can only ever see two paths: /root and /mounted — both mapped directly to your own namespace. The real server filesystem, other users’ namespaces, and Ocalt’s internal infrastructure are completely invisible. There is no path traversal, no symlink escape, and no shared state with the host.

What runs inside the sandbox

Every WEBASSEMBLY block executes in an isolated jail. It sees your namespace as /root and /mounted and nothing else of the machine - no other account and no host filesystem. Its network is its own rather than the machine’s: the public internet is reachable from inside it, and what else is reachable is described under The sandbox has a network below.

TYPE What is available
"php"The full extension set - gd, imagick, zip, curl, mbstring, intl, bcmath, sodium, openssl, xml, dom, sqlite3, pdo, and the rest. Your code is appended to an opening <?php, so do not write the tag yourself.
"python"Pillow, NumPy, pandas, SciPy, scikit-learn, statsmodels, SymPy, NetworkX, Matplotlib, OpenCV, requests, BeautifulSoup, lxml, PyYAML, cryptography, openpyxl, python-docx, reportlab, pypdf, qrcode - plus PyTorch, torchvision, Transformers, sentence-transformers and ONNX Runtime for machine learning.
"node"lodash, axios, cheerio, node-fetch, dayjs, uuid, js-yaml, marked, mathjs, jimp, qrcode, csv-parse, csv-stringify.
"ruby"json, nokogiri, httparty.
"perl"JSON, XML::LibXML, LWP.
"bash"A shell inside the same jail, with the standard userland.
"java"JDK 11 in single-file source mode. Your statements run inside a generated main(), so write plain statements - no class or method declaration. java.util and java.io are imported already.
"rust"A complete program, compiled when it runs, with the Rust standard library. Write fn main() yourself - statements on their own produce nothing. Crates outside the standard library, such as serde_json or regex, are not installed.
"go"A complete program, compiled when it runs, with the Go standard library. Write package main and func main() yourself - statements on their own produce nothing. A module imported from a public repository is downloaded on first use.
"wasm"A compiled .wasm module from your namespace, executed directly.
Drawing an image with PHP GD
WEBASSEMBLY TYPE "php" ENTER `
$im = imagecreatetruecolor(200, 120);
$bg  = imagecolorallocate($im, 20, 30, 60);
$fg  = imagecolorallocate($im, 232, 182, 44);
imagefilledrectangle($im, 0, 0, 199, 119, $bg);
imagefilledellipse($im, 100, 60, 80, 80, $fg);
imagepng($im, "/root/badge.png");
echo filesize("/root/badge.png") . " bytes";
` SET ?result
AFTER EMIT ?result
Numerical work with Python
WEBASSEMBLY TYPE "python" ENTER `
import numpy as np
from sklearn.linear_model import LinearRegression
x = np.array([[1],[2],[3],[4]])
y = np.array([3, 5, 7, 9])
m = LinearRegression().fit(x, y)
print("slope", round(float(m.coef_[0]), 3), "intercept", round(float(m.intercept_), 3))
` SET ?result
AFTER EMIT ?result
A tensor operation with PyTorch
WEBASSEMBLY TYPE "python" ENTER `
import torch
a = torch.tensor([[1., 2.], [3., 4.]])
print("determinant", float(torch.det(a)))
` SET ?result
AFTER EMIT ?result
Java
STRING "Ocalt" SET ?name
AFTER WEBASSEMBLY GRANT ?name AS "name"
AFTER WEBASSEMBLY TYPE "java" ENTER `
List<String> parts = new ArrayList<>(Arrays.asList("c", "a", "b"));
Collections.sort(parts);
System.out.println("hello " + oql.get("name") + " " + String.join(",", parts));
` SET ?result
AFTER EMIT ?result
(* Output: hello Ocalt a,b,c *)
Java runs in single-file source mode: write statements, not a class. They are placed inside a generated main(), with java.util and java.io already imported and the grants available as a Map<String,String> called oql.
Rust
STRING "Ocalt" SET ?name
AFTER WEBASSEMBLY GRANT ?name AS "name"
AFTER WEBASSEMBLY TYPE "rust" ENTER `
fn main() {
    let name = std::env::var("OQL_name").unwrap_or_default();
    let mut parts = vec!["c", "a", "b"];
    parts.sort();
    println!("hello {} {}", name, parts.join(","));
}
` SET ?result
AFTER EMIT ?result
(* Output: hello Ocalt a,b,c *)
Go
STRING "Ocalt" SET ?name
AFTER WEBASSEMBLY GRANT ?name AS "name"
AFTER WEBASSEMBLY TYPE "go" ENTER `
package main

import (
	"fmt"
	"os"
	"sort"
	"strings"
)

func main() {
	parts := []string{"c", "a", "b"}
	sort.Strings(parts)
	fmt.Println("hello", os.Getenv("OQL_name"), strings.Join(parts, ","))
}
` SET ?result
AFTER EMIT ?result
(* Output: hello Ocalt a,b,c *)
Rust and Go take a whole program, not statements: fn main() for Rust, package main with func main() for Go. Grants reach both as environment variables named OQL_ followed by the alias, the same way bash and wasm receive them.
The sandbox has a network - its own. A WEBASSEMBLY block is not cut off from the world; it is placed in a network that contains only your things. 127.0.0.1 is its own loopback, nothing of Ocalt’s is on it, and no other namespace is reachable because none is attached to it. Outbound connections to the public internet are open, so an HTTPS request to any public host succeeds, a library that downloads on first use works, and pip works. A request to your own database by name works, once you have mapped it.

Serving — a process that stays up

WEBASSEMBLY TYPE runs something and waits for it to finish. WEBASSEMBLY SERVE does not wait. The process binds a port inside your namespace's own network and keeps running after the script that started it has returned - which is what an ordinary backend expects to do.

This is how a Node, Java, Python or compiled backend you already have runs here unchanged. It listens on a port; the namespace network gives that port an address; nothing outside your namespace can reach it.

Starting a Node backend
WEBASSEMBLY SERVE "api" TYPE "node" ENTER "/root/app/server.js" ON 3000 SET ?p
AFTER EMIT ?p("name") & " is up on port " & ?p("port")

The name is yours to choose and is how the process is addressed afterwards. Starting the same name again replaces what was there, rather than leaving an orphan holding the port.

A Java service, and a compiled binary
WEBASSEMBLY SERVE "orders" TYPE "java" ENTER "/root/app/orders.jar" ON 8080 SET ?j
AFTER WEBASSEMBLY SERVE "engine" TYPE "binary" ENTER "/root/bin/engine" ON 9000 SET ?e
AFTER EMIT ?j("pid") & " and " & ?e("pid")

PORT is set in the process's environment, the way every hosting platform sets it, so an app that reads process.env.PORT needs no change at all.

Reaching it

A process is a target the namespace network can name, the same as a site or a database. Once named, anything inside your namespace reaches it by that name.

Naming a running process
WEBASSEMBLY SERVE "api" TYPE "node" ENTER "/root/app/server.js" ON 3000 SET ?p
AFTER NETWORK MAP "api.internal" TO PROCESS "api" SET ?r
AFTER EMIT ?r("address")

What is running

Listing and stopping
WEBASSEMBLY PROCESSES SET ?procs
AFTER FOREACH ?procs SET ?p
OPEN
  EMIT ?p("name") & " on " & ?p("port") & " — "
  AFTER IF ?p("running") IS EQUAL TO true
  OPEN
    EMIT "running"
  CLOSE
  OR
  OPEN
    EMIT "stopped"
  CLOSE
CLOSE

AFTER WEBASSEMBLY STOP "api" SET ?ok
A served process writes its output to /root/.ql/<name>.log in your own namespace, so a crash is readable with FILE READ like any other file. Processes do not survive a restart of the platform itself - start them again from a CRON if something must always be up.
VerbDescription
WEBASSEMBLY SERVE "name" TYPE "lang" ENTER "path" ON portStarts a process that keeps running. Returns name, pid, port and log path
WEBASSEMBLY PROCESSES SET ?pEvery process you have started, and whether it is still running
WEBASSEMBLY STOP "name"Stops one and forgets it