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
SSH "root" AT "123.45.67.89" PASSWORD "mypassword" ENTER `ls /var/www` SET ?result
AFTER EMIT ?result("stdout")
Key-based authentication
SSH "deploy" AT "123.45.67.89" KEY "/root/keys/id_rsa" ENTER `git pull` SET ?result
AFTER EMIT ?result("stdout")
Custom port
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.
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
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
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
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 |
|---|---|---|
stdout | string | Standard output from the command |
stderr | string | Standard error from the command |
exit_code | number | Process exit code — 0 is success |
| Verb | Description |
|---|---|
SSH "user" AT "host" PASSWORD "pass" ENTER `cmd` SET ?r | Single command via password auth. Default port 22. |
SSH "user" AT "host" KEY "/root/key" ENTER `cmd` SET ?r | Single command via key auth. |
SSH "user" AT "host" ON "port" [AUTH] ENTER `cmd` SET ?r | Custom 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 ?path | Copy 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.
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.
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.
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'] |
| python | oql['alias'] |
| node | oql.alias |
| ruby | oql['alias'] |
| perl | $oql{'alias'} |
| java | oql.get("alias") |
| bash | $OQL_alias (env var) |
| rust | std::env::var("OQL_alias") (env var) |
| go | os.Getenv("OQL_alias") (env var) |
| wasm | passed 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.
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. |
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
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
WEBASSEMBLY TYPE "python" ENTER `
import torch
a = torch.tensor([[1., 2.], [3., 4.]])
print("determinant", float(torch.det(a)))
` SET ?result
AFTER EMIT ?result
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 *)
main(), with java.util and java.io already imported and the grants available as a Map<String,String> called oql.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 *)
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 *)
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.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.
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.
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.
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
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
/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.| Verb | Description |
|---|---|
WEBASSEMBLY SERVE "name" TYPE "lang" ENTER "path" ON port | Starts a process that keeps running. Returns name, pid, port and log path |
WEBASSEMBLY PROCESSES SET ?p | Every process you have started, and whether it is still running |
WEBASSEMBLY STOP "name" | Stops one and forgets it |