Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,7 @@ exhaustively at the top of [`lua_interop.lua`](lua_interop.lua).
The flagship is **[`examples/openresty/`](examples/openresty/)** — a complete
guestbook web app whose validation rules are written once in Shen and run on
*both* ends: as a typechecked core on the server (shen-lua inside OpenResty) and
as a [Ratatoskr](https://github.com/pyrex41/ratatoskr)-shaken,
as a [Yggdrasil](https://github.com/pyrex41/yggdrasil)-shaken,
[ShenScript](https://github.com/pyrex41/ShenScript)-compiled module in the
browser. One `rules.shen`, two runtimes, no client/server drift. See its
[README](examples/openresty/README.md) for the walkthrough.
Expand All @@ -358,7 +358,7 @@ browser. One `rules.shen`, two runtimes, no client/server drift. See its
| [`examples/policy/`](examples/policy/) | a typed **authorization** gateway: one rule set enforced at the OpenResty edge and previewed in the browser, plus authz-as-type-inhabitation — a permission *is* a proof ([README](examples/policy/README.md)) |
| [`examples/crdt/`](examples/crdt/) | a **CRDT** sync hub: replicas converge via a typed join-semilattice merge whose laws are checked by execution *and* by machine-checked sequent-calculus proof ([README](examples/crdt/README.md)) |
| [`examples/pcr/`](examples/pcr/) | **proof-carrying requests over live facts**: the client carries a proof term, the OpenResty gate *checks* it — never searches — against a versioned fact store consulted at proof time, so revoking one fact makes the same proof bytes fail on the next request while delegation chains stay composable and every allow logs its justification ([README](examples/pcr/README.md)) |
| [`examples/openresty/`](examples/openresty/) | a **complete web app in Shen on OpenResty** (nginx + LuaJIT): typed request validators + a Shen router behind a JSON API, with a front end that runs the **same** typed rules in the browser — Ratatoskr-shaken and ShenScript-compiled to a ~140 KB module. One `rules.shen`, validated client- and server-side. Runs standalone (`luajit examples/openresty/selftest.lua`) or under `openresty` ([README](examples/openresty/README.md)) |
| [`examples/openresty/`](examples/openresty/) | a **complete web app in Shen on OpenResty** (nginx + LuaJIT): typed request validators + a Shen router behind a JSON API, with a front end that runs the **same** typed rules in the browser — Yggdrasil-shaken and ShenScript-compiled to a ~140 KB module. One `rules.shen`, validated client- and server-side. Runs standalone (`luajit examples/openresty/selftest.lua`) or under `openresty` ([README](examples/openresty/README.md)) |
| [`examples/openresty-authz/`](examples/openresty-authz/) | durable multi-tenant **authorization**: the policy as a Prolog proof chain (`token → user → tenant → resource`), a typed `decision` witness that gates every response, and an event-sourced store (file + `lua-resty-lmdb`) whose append-only log makes decisions durable and auditable ([README](examples/openresty-authz/README.md)) |
| [`examples/envoy/`](examples/envoy/) | **Shen at the edge**: Envoy fronting both apps above — `ext_authz` runs every request through the authz proof chain (edge decisions durably audited), and an Envoy **Lua filter** runs the same typed `rules.shen` inside the proxy, so malformed requests get their typed 400 before costing an upstream hop ([README](examples/envoy/README.md)) |

Expand Down
36 changes: 18 additions & 18 deletions bin/ratatoskr-build.lua → bin/yggdrasil-build.lua
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
-- bin/ratatoskr-build.lua : Ratatoskr stage-2 builder for shen-lua.
-- bin/yggdrasil-build.lua : Yggdrasil stage-2 builder for shen-lua.
--
-- luajit bin/ratatoskr-build.lua <shaken-dir> <out.lua> [--linked]
-- luajit bin/yggdrasil-build.lua <shaken-dir> <out.lua> [--linked]
--
-- <shaken-dir> is a Ratatoskr stage-1 output directory: a tree-shaken
-- <shaken-dir> is a Yggdrasil stage-1 output directory: a tree-shaken
-- kernel (kernel.kl, ShenOSKernel-41.2 defuns in load order), the user
-- program as KL (one or more user= files), and ratatoskr.manifest.txt.
-- program as KL (one or more user= files), and yggdrasil.manifest.txt.
-- The builder compiles every KL form ahead of time with the port's own
-- compiler (compiler.lua C.compile_top) and emits ONE runnable Lua
-- program <out.lua>:
Expand Down Expand Up @@ -57,7 +57,7 @@
-- generated program needs no KDATA serialization/rebuild step.

-- ---- locate the repo root from this script's own path ---------------------
local self = arg and arg[0] or "bin/ratatoskr-build.lua"
local self = arg and arg[0] or "bin/yggdrasil-build.lua"
local root = self:match("^(.*)[/\\]bin[/\\][^/\\]+$") or "."
if not root:match("^[/\\]") and not root:match("^%a:[/\\]") then
-- absolutize so --linked outputs bake a path that works from any cwd
Expand All @@ -71,7 +71,7 @@ if not root:match("^[/\\]") and not root:match("^%a:[/\\]") then
end
package.path = root .. "/?.lua;" .. package.path

local USAGE = "usage: luajit bin/ratatoskr-build.lua <shaken-dir> <out.lua> [--linked]\n"
local USAGE = "usage: luajit bin/yggdrasil-build.lua <shaken-dir> <out.lua> [--linked]\n"

local shaken_dir, outpath, linked
for i = 1, #arg do
Expand All @@ -80,7 +80,7 @@ for i = 1, #arg do
elseif a == "-h" or a == "--help" then io.write(USAGE); os.exit(0)
elseif not shaken_dir then shaken_dir = a
elseif not outpath then outpath = a
else io.stderr:write("ratatoskr-build: unexpected argument " .. a .. "\n" .. USAGE); os.exit(2) end
else io.stderr:write("yggdrasil-build: unexpected argument " .. a .. "\n" .. USAGE); os.exit(2) end
end
if not (shaken_dir and outpath) then
io.stderr:write(USAGE); os.exit(2)
Expand All @@ -95,7 +95,7 @@ local function read_file(path)
end

-- ---- 1. parse the manifest -------------------------------------------------
local MANIFEST = shaken_dir .. "/ratatoskr.manifest.txt"
local MANIFEST = shaken_dir .. "/yggdrasil.manifest.txt"
local man = { user = {}, primitive = {} }
for line in read_file(MANIFEST):gmatch("[^\r\n]+") do
local k, v = line:match("^([%w%-]+)=(.*)$")
Expand All @@ -109,7 +109,7 @@ assert(man.kernel, MANIFEST .. ": missing kernel=")
assert(man.init, MANIFEST .. ": missing init=")
assert(#man.user > 0, MANIFEST .. ": no user= entries")
if man["kernel-version"] ~= "41.2" then
io.stderr:write(("ratatoskr-build: warning: manifest kernel-version=%s, this port is certified against 41.2\n")
io.stderr:write(("yggdrasil-build: warning: manifest kernel-version=%s, this port is certified against 41.2\n")
:format(tostring(man["kernel-version"])))
end

Expand Down Expand Up @@ -137,7 +137,7 @@ local BOOT_GLOBALS = {
local GUARDED_DEAD = { ["shen.write-string"]=true, ["shen.read-unit-string"]=true }
for _, name in ipairs(man.primitive) do
if not (P.F[name] or SPECIAL[name] or BOOT_GLOBALS[name] or GUARDED_DEAD[name]) then
io.stderr:write("ratatoskr-build: warning: manifest primitive not provided by this port: "
io.stderr:write("yggdrasil-build: warning: manifest primitive not provided by this port: "
.. name .. "\n")
end
end
Expand All @@ -161,7 +161,7 @@ end
-- in the shaken output. Any such name found in the port's vendored certified
-- 41.2 kernel (klambda/*.kl) is BACKFILLED into the kernel chunk — with a
-- loud warning, because each backfill is a stage-1 shaker bug that should be
-- fixed in ratatoskr.shen. Names found nowhere are warn-only (they may be
-- fixed in yggdrasil.shen. Names found nowhere are warn-only (they may be
-- guarded-dead, like shen.write-string behind shen.char-stoutput?).
-- Limitation (same one stage 1 has): only head-position references are
-- traced; a function passed by bare name in argument position is invisible.
Expand Down Expand Up @@ -239,7 +239,7 @@ do
if not (defined[n] or P.F[n] or SPECIAL[n]) then
local form = certified[n]
if form then
io.stderr:write("[ratatoskr] STAGE-1 UNDER-SHAKE: " .. n
io.stderr:write("[yggdrasil] STAGE-1 UNDER-SHAKE: " .. n
.. " is called but missing from " .. man.kernel
.. "; backfilling from certified kernel\n")
kernel_forms[#kernel_forms+1] = form
Expand All @@ -258,7 +258,7 @@ do
end
end
for _, n in ipairs(unresolved) do
io.stderr:write("ratatoskr-build: warning: " .. n
io.stderr:write("yggdrasil-build: warning: " .. n
.. " is referenced but provided nowhere (may be guarded-dead)\n")
end
end
Expand Down Expand Up @@ -304,11 +304,11 @@ local function compile_chunks(forms, label)
return chunks
end

io.stderr:write(("[ratatoskr] compiling %s: %d kernel forms\n"):format(man.kernel, #kernel_forms))
io.stderr:write(("[yggdrasil] compiling %s: %d kernel forms\n"):format(man.kernel, #kernel_forms))
local kernel_chunks = compile_chunks(kernel_forms, man.kernel)
local user_chunks = {}
for _, uf in ipairs(user_files) do
io.stderr:write(("[ratatoskr] compiling %s: %d user forms\n"):format(uf.name, #uf.forms))
io.stderr:write(("[yggdrasil] compiling %s: %d user forms\n"):format(uf.name, #uf.forms))
for _, ch in ipairs(compile_chunks(uf.forms, uf.name)) do
user_chunks[#user_chunks+1] = ch
end
Expand All @@ -328,7 +328,7 @@ local out = {}
local function emit(s) out[#out+1] = s end

local jitv = rawget(_G, "jit")
emit(("-- %s : shaken Shen program (generated by bin/ratatoskr-build.lua)\n"):format(
emit(("-- %s : shaken Shen program (generated by bin/yggdrasil-build.lua)\n"):format(
outpath:match("[^/\\]+$") or outpath))
emit(("-- source: %s (kernel-version=%s, %d kernel defuns/forms, user: %s)\n"):format(
shaken_dir, tostring(man["kernel-version"]), #kernel_forms, table.concat(man.user, ", ")))
Expand All @@ -349,7 +349,7 @@ local loadstr = loadstring or load
for name, src in pairs(sources) do
if package.loaded[name] == nil then
package.preload[name] = function(...)
return assert(loadstr(src, "@ratatoskr/" .. name .. ".lua"))(...)
return assert(loadstr(src, "@yggdrasil/" .. name .. ".lua"))(...)
end
end
end
Expand Down Expand Up @@ -453,5 +453,5 @@ local fh = assert(io.open(outpath, "wb"))
local blob = table.concat(out)
fh:write(blob)
fh:close()
io.stderr:write(("[ratatoskr] wrote %s (%d bytes, %d kernel + %d user chunks%s)\n")
io.stderr:write(("[yggdrasil] wrote %s (%d bytes, %d kernel + %d user chunks%s)\n")
:format(outpath, #blob, #kernel_chunks, #user_chunks, linked and ", linked" or ", self-contained"))
2 changes: 1 addition & 1 deletion examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ has a `selftest.lua` that runs off-nginx):
| [`policy/`](policy/) | a typed **authorization** gateway: one rule set enforced at the OpenResty edge and previewed in the browser, plus authz-as-type-inhabitation (a permission *is* a proof). `luajit examples/policy/selftest.lua` |
| [`crdt/`](crdt/) | a **CRDT** sync hub: replicas converge via a typed join-semilattice merge whose laws are checked by execution *and* by machine-checked sequent-calculus proof. `luajit examples/crdt/selftest.lua` |
| [`pcr/`](pcr/) | **proof-carrying requests over live facts**: the client attaches a proof term, the OpenResty gate *checks* it — never searches — against a versioned fact store consulted at proof time, so revoking one fact makes the same proof bytes fail on the next request while delegation chains stay composable and every allow logs its full justification. `luajit examples/pcr/selftest.lua` |
| [`openresty/`](openresty/) | a complete web app — typed Shen validators + a Shen router on OpenResty (nginx + LuaJIT), with a front end that runs the **same** rules in the browser (Ratatoskr-shaken, ShenScript-compiled). Runs standalone via `luajit examples/openresty/selftest.lua`; see [its README](openresty/README.md) to serve it. |
| [`openresty/`](openresty/) | a complete web app — typed Shen validators + a Shen router on OpenResty (nginx + LuaJIT), with a front end that runs the **same** rules in the browser (Yggdrasil-shaken, ShenScript-compiled). Runs standalone via `luajit examples/openresty/selftest.lua`; see [its README](openresty/README.md) to serve it. |
| [`openresty-authz/`](openresty-authz/) | durable multi-tenant **authorization**: the policy as a Prolog proof chain (`token → user → tenant → resource`), a typed `decision` witness that gates every response, and an event-sourced store (file + `lua-resty-lmdb`) whose append-only log makes decisions durable and auditable. Runs standalone via `luajit examples/openresty-authz/selftest.lua`; see [its README](openresty-authz/README.md). |
| [`envoy/`](envoy/) | **Shen at the edge**: Envoy in front of both apps above — its `ext_authz` filter sends every request through the authz app's proof chain (edge decisions land in the same durable audit log), and an Envoy **Lua filter** runs the guestbook's typed `rules.shen` *inside the proxy* (LuaJIT), rejecting malformed bodies at the edge with the origin's exact error strings. One typed rule file, four hosts. Runs standalone via `luajit examples/envoy/selftest.lua`; see [its README](envoy/README.md). |

Expand Down
2 changes: 1 addition & 1 deletion examples/envoy/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ integration seams for exactly what it is good at:
costs an upstream hop.

Which makes it one `rules.shen`, enforced on **four hosts** from one typed
source: the browser (ShenScript, Ratatoskr-shaken), the Envoy edge (shen-lua on
source: the browser (ShenScript, Yggdrasil-shaken), the Envoy edge (shen-lua on
Envoy's LuaJIT), the origin (shen-lua on OpenResty), and plain `luajit` in the
selftests. Proved sound by the sequent-calculus typechecker wherever shen-lua
loads it.
Expand Down
10 changes: 5 additions & 5 deletions examples/openresty/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ host, so the backend can literally be Shen with a thin Lua glue layer. The front
end runs Shen too: the browser validates with a build of the **same**
`rules.shen`, compiled to JavaScript by
[ShenScript](https://github.com/pyrex41/ShenScript) and tree-shaken by
[Ratatoskr](https://github.com/pyrex41/ratatoskr), so the field rules are
[Yggdrasil](https://github.com/pyrex41/yggdrasil), so the field rules are
checked client-side AND server-side from one source of truth.

```
Expand Down Expand Up @@ -152,7 +152,7 @@ network); only a client-valid entry is POSTed, where the server re-runs
That client module is not the whole ShenScript kernel — it is a **tree-shaken
build of `rules.shen`**:

1. [Ratatoskr](https://github.com/pyrex41/ratatoskr), a Shen tree-shaker, walks
1. [Yggdrasil](https://github.com/pyrex41/yggdrasil), a Shen tree-shaker, walks
the kernel call graph and emits only the ~100 kernel functions these rules
can reach. Because the rules never touch `eval`/`read`/`tc`, the reader, the
macro expander, the typechecker and `eval` itself all fall away
Expand Down Expand Up @@ -183,10 +183,10 @@ The committed `shen-rules.client.js` is generated; rerun the build whenever
examples/openresty/scripts/build-client.sh
```

It needs sibling checkouts of [Ratatoskr](https://github.com/pyrex41/ratatoskr)
(the `ratatoskr` binary) and [ShenScript](https://github.com/pyrex41/ShenScript),
It needs sibling checkouts of [Yggdrasil](https://github.com/pyrex41/yggdrasil)
(the `yggdrasil` binary) and [ShenScript](https://github.com/pyrex41/ShenScript),
plus `luajit` (the shake host) and Node 20+. Override locations with
`$RATATOSKR` and `$SHENSCRIPT_DIR`. The script concatenates `rules.shen` +
`$YGGDRASIL` and `$SHENSCRIPT_DIR`. The script concatenates `rules.shen` +
`scripts/client.glue.shen`, shakes the slice, and compiles it to the module.

The running page is self-documenting too: a "What this demonstrates" panel with
Expand Down
12 changes: 6 additions & 6 deletions examples/openresty/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
examples/openresty/public/index.html — the guestbook front end.

This page runs Shen IN THE BROWSER. It imports vendor/shen-rules.client.js — a
Ratatoskr-shaken, ShenScript-compiled build of the SAME rules.shen the server
Yggdrasil-shaken, ShenScript-compiled build of the SAME rules.shen the server
loads — and runs validate-message client-side for instant feedback. The server
then re-runs the identical rules as the authoritative check.

"Shaken" = tree-shaken: Ratatoskr (https://github.com/pyrex41/ratatoskr) walks
"Shaken" = tree-shaken: Yggdrasil (https://github.com/pyrex41/yggdrasil) walks
the Shen kernel call graph and emits only the ~100 functions rules.shen can
reach, so the client module is ~140 KB and inits in ~20 ms — versus the full
~660 KB ShenScript kernel bundle booting in ~2.3 s. The artifact is generated
Expand Down Expand Up @@ -53,7 +53,7 @@
<h1>Guestbook <small>— validated by Shen, on both ends</small></h1>
<p>The field rules live in one file,
<a href="/rules.shen" target="_blank"><code>rules.shen</code></a>, and run
<b>twice</b>: in your browser (a <a href="https://github.com/pyrex41/ratatoskr">Ratatoskr</a>-shaken,
<b>twice</b>: in your browser (a <a href="https://github.com/pyrex41/yggdrasil">Yggdrasil</a>-shaken,
<a href="https://github.com/pyrex41/ShenScript">ShenScript</a>-compiled build of those rules) for
instant feedback, and on the server (shen-lua under OpenResty) as the
authoritative check. Sign the guestbook below — then see
Expand Down Expand Up @@ -85,7 +85,7 @@ <h2>What this demonstrates</h2>
sequent-calculus typechecker proves every rule sound <i>before the
first request</i> — a type error aborts boot.</li>
<li><b>Tree-shaken for fast loading.</b>
<a href="https://github.com/pyrex41/ratatoskr">Ratatoskr</a> walks the
<a href="https://github.com/pyrex41/yggdrasil">Yggdrasil</a> walks the
Shen kernel call graph and emits only the ~100 functions these rules
can reach (eval, the reader and the typechecker fall away). The client
module is ~140 KB and inits in tens of ms, versus the full ~660 KB
Expand All @@ -101,7 +101,7 @@ <h2>What this demonstrates</h2>
Lua interop.</li>
</ul>
<pre>
BROWSER — ShenScript (Shen → JS), tree-shaken by Ratatoskr
BROWSER — ShenScript (Shen → JS), tree-shaken by Yggdrasil
you fill the form
Expand Down Expand Up @@ -161,7 +161,7 @@ <h2>What this demonstrates</h2>
const t0 = performance.now();
checkFields = await createValidator();
const ms = Math.round(performance.now() - t0);
setStatus(`<b>Shen ready in the browser</b> (${ms} ms) — a Ratatoskr-shaken `
setStatus(`<b>Shen ready in the browser</b> (${ms} ms) — a Yggdrasil-shaken `
+ `build of <code>rules.shen</code>, the same rules the server runs.`);
$$("submit").disabled = false;
} catch (e) {
Expand Down
4 changes: 2 additions & 2 deletions examples/openresty/public/vendor/shen-rules.client.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
// GENERATED — do not edit. Built by examples/openresty/scripts/build-client.{sh,mjs}
// from rules.shen (+ client.glue.shen) via Ratatoskr (Shen tree-shaker) and
// from rules.shen (+ client.glue.shen) via Yggdrasil (Shen tree-shaker) and
// ShenScript's compiler. Regenerate with: examples/openresty/scripts/build-client.sh
// kernel defuns: 99; user: client-prog.kl; needs-eval: false
// Self-contained: runtime.js + overrides.js are embedded; no imports, no checkout needed at runtime.
// The KLambda runtime: everything compiled kernel/user code references on $,
// with no compiler. This module deliberately has ZERO imports and a single
// default export so build tools (bin/ratatoskr-build.js) can embed its source
// default export so build tools (bin/yggdrasil-build.js) can embed its source
// verbatim by replacing "export default" with a const declaration.
// eval-kl raises unless a compiler layer (lib/backend.js) is attached.

Expand Down
Loading
Loading