Program your Database
ScramDB is a UTAP (Unified Transactional Analytical Processing) database: transactions, analytics, and AI/search all run on one live copy of your data. Programmability extends that same engine with functions you write yourself, callable straight from SQL and running right next to your data, with no export and no second system to keep in sync.
A ScramDB function runs inside afterburner, a deterministic, polyglot WebAssembly runtime built into the engine. Functions are:
- Sealed by default. A function cannot reach the network, the filesystem, or environment variables. It only ever sees the arguments you pass it, and returns a result. Nothing else. Every installed package runs fully sealed regardless of what its own capability grant declares, so this is not a promise you have to trust the package author to keep.
- Deterministic. The same inputs always produce the same outputs. No clocks, no randomness, no hidden state.
- Governed. JavaScript functions run inside a sandbox with fuel, memory, timeout, and output-size limits, so a heavy or misbehaving function can never destabilize the database.
- Callable from SQL. Once registered, a function is just a function:
SELECT my_function(col) FROM my_table.
Because ScramDB is UTAP, these functions see your transactional writes the instant they land. There is no pipeline to build and no copy to keep current.
Two ways to add a functionβ
There are two ways to get your own code running inside ScramDB, and which one you reach for depends on how much code you have:
- Inline source. Paste the function body straight into
CREATE FUNCTION ... AS $$...$$. No build step, no artifact, no registry. This works forLANGUAGE js,LANGUAGE python, andLANGUAGE ruby. - Packages. Author, build, and publish a self-contained artifact (a
.afbfile) with theburntoolchain, install it by name withscram.install, then bind it to a SQL function withCREATE FUNCTION ... LANGUAGE js AS 'namespace/pkg'. This is the path for multi-file projects, for anything you want to share or version, and it is the only way to run code written in a compiled language such as Rust, Go, C, or C++.
ScramDB also ships 24 functions across 15 package families as preinstalled packages you can bind and call in two statements, without writing or building anything. See What you can build below.
Write in any languageβ
CREATE FUNCTION ... LANGUAGE <word> recognizes four language keywords:
LANGUAGE keyword | Availability today | Inline source (AS $$...$$) |
|---|---|---|
js | Works | Yes |
python | Works | Yes |
ruby | Works | Yes |
ts | Parses, but every call is refused in this build | No |
ts is a real, recognized keyword, not a typo you might make: the engine simply does not yet install the transpile step it needs, so any CREATE FUNCTION ... LANGUAGE ts is refused with an explanation. Use js instead; the two share the same runtime and calling convention.
Rust, Go, C, and C++ are package-authoring languages, not LANGUAGE keywords. You cannot write LANGUAGE rust AS $$...$$. Instead, you author a package in one of these languages, build it to a .afb artifact with burn, install the artifact with scram.install, and bind it with CREATE FUNCTION ... LANGUAGE js AS 'namespace/pkg' (the LANGUAGE js here names the binding form, not the language the package's source was written in). See Writing a package for the calling convention a compiled package must follow, and Install and use a package for the install-then-bind flow.
Separately from all of this, ScramDB also has a pre-existing, ordinary SQL routine system: LANGUAGE sql and LANGUAGE plpgsql define plain functions directly in SQL. That system is not part of afterburner, has no sandboxing or packages, and is documented in the SQL reference rather than here. Keep the two mechanisms distinct in your head: sql/plpgsql are ordinary SQL routines; js/python/ruby (and packages bound through js) are afterburner-executed functions with the sealing and governance described above.
What you can buildβ
ScramDB ships 15 starter package families, covering search, AI, geospatial, clustering, statistics, and privacy. Install any of them with one scram.install(...) call, then bind the function you want with CREATE FUNCTION (see Install and use a package), or use them as worked examples for packages of your own:
| Family | What it does |
|---|---|
embeddings | Vector similarity for AI and semantic search (cosine similarity, dot product) |
bm25 | Okapi BM25 relevance scoring for ranking search results |
fts | Full-text search helpers such as term frequency |
geo | Geospatial math: great-circle (haversine) distance and geohash encoding |
h3 | Hexagonal grid spatial indexing |
kmeans | K-means cluster assignment |
dbscan | Density-based clustering (neighbor counts) |
regression | Ordinary least squares linear regression |
anomaly | Anomaly detection (z-score and median-absolute-deviation score) |
sketch | HyperLogLog approximate distinct counts |
theta | Theta sketches for set cardinality and overlap |
topk | Top-k most frequent items |
quantile | Streaming quantile and percentile estimates |
anonymize | Pseudonymization: salted SHA-256 hashing and masking |
jsonschema | JSON Schema validation |
Vectors are stored as JSON arrays and compared by the embeddings similarity functions, so semantic search runs on the same rows as everything else, no separate vector store required. There is no native vector or array column type today; every starter package that works with vectors stores one as a JSON array inside a text column and parses it itself. The same limit applies to any package you write.
The starter set also includes rust_sha256, a package compiled ahead of time from Rust rather than interpreted from JavaScript source. It is the reference example for the compiled-language calling convention described in Writing a package.
How it fits togetherβ
For a package (the path for multi-file projects and compiled languages):
- Write a package: a manifest, a capability grant, and your source. See Writing a package.
- Build it into a single, content-addressed
.afbartifact withburn. - Install the artifact with
SELECT scram.install(...). - Bind it to a SQL function with
CREATE FUNCTION ... LANGUAGE js AS 'namespace/pkg', then call it like any other function. See Install and use a package.
For inline source, skip straight to CREATE FUNCTION my_fn(...) RETURNS ... LANGUAGE js AS $$ ... $$; (or python / ruby) with no build or install step at all.