Repository navigation
lua: Lua 5.5 as a crate, with io, os and kore.fs - #23
Merged
Merged
Conversation
The engine, its freestanding host and the `kore.*` standard library move
out of Kore's `user/apps/utils/lua` into a crate any libkore consumer can
embed, beside `kore-wasi` and on the same seam.
`io` and `os` are written here rather than vendored: Lua's `liolib.c` and
`loslib.c` are not compiled at all, because both are libc over POSIX and
this target has neither. They are a veneer over one implementation, not a
second one — `io.open` and `kore.fs.open` answer the same file object, and
one failure is rendered two ways, `nil, message, errno` on the stock
surface and `nil, {code = "not-found"}` on the kore one, both derived from
`FsError` rather than invented.
Twenty of the twenty-two members the manual lists are present, with all
seven `file:` methods. `os.execute` and `io.popen` are the two absent:
both launch a process, which is a service face the embedding program holds
and this crate deliberately does not, so they arrive through `Extension`
like `kore.accel`.
The stream position and its read-ahead live in the file object rather than
beside the view, so there is one authority for it; the host side is purely
positional. A handle is an index and a generation, so a use after close is
`InvalidHandle` rather than a read of whatever took the slot.
Where the manual leaves a detail to the system this build chooses and says
so in KORE-VENDOR.md: `os.date` computes civil time directly, there being
no `gmtime` or timezone database below it, so the one clock reads UTC;
`os.clock` is monotonic since the VM started; `os.getenv` answers nil for
every name, a task's authority arriving as capabilities rather than
strings; `os.tmpname` creates its file exclusively, and `io.tmpfile`
removes its own when the handle retires.
The transport grows one borrowed byte window so a path or a write's bytes
cross without a copy; no new word state, and a verb is still a constant and
an arm. `Extension` loses its `'static` bound, which forbade the one shape
an embedder has — a face borrowed from the `Rt` and transport its `main`
owns — and bought nothing, the worker reading only `LIBRARIES`.
The host smoke now runs the whole surface on every build, including the
calendar, which is pure computation and needs no machine. It also gets the
right prototype for `kore_lua_new`: it declared three parameters against a
definition taking five, so every smoke run passed two garbage registers
that `luaopen_kore` then walked as a library array.
Gates: host smoke, host clippy `-D warnings`, unit tests and `cargo fmt`
clean; `make cross` clean on x86_64, aarch64 and riscv64gc, and now
includes this crate, so a target-only regression in the amalgamation meets
a compiler instead of a keytest.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
SnowCheetos
marked this pull request as draft
September 24, 2026 17:00
Copilot stopped reviewing on behalf of
SnowCheetos due to an error
September 24, 2026 17:00
julian-avar
marked this pull request as ready for review
September 29, 2026 13:03
`kore-lua`'s build script queried Zig before anything else, so every host `cargo test`/`cargo miri test` of the workspace failed on a machine with no Zig — including CI's `host` and `miri` jobs, which install no Zig at all. The engine exists only where `src/lib.rs` compiles its C half (`target_os = "none"`), so a host build has no object to link and no reason to need Zig: return before the toolchain query when the target is not Kore's. The C smoke moves ahead of that early return and becomes skippable: it needs only a host C compiler, and a consumer with neither toolchain must still be able to build this workspace. A compiler that is present but fails is still fatal. `tests/host_fs.c` gives the smoke an in-memory store, so the whole `io`/`os`/`kore.fs` surface — `stat` included — runs without a machine. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The 9e4172bc2 re-export added both crates to `native/Cargo.toml` without their lockfile entries, so every `--locked` command against the native workspace refused to run — `make test`'s second invocation, and Audit's lockfile-currency gate. Resolution adds `elf 0.8.0`; everything else the two crates want was already locked. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two advisories landed against Wasmtime 47 on 2026-09-24: `call_ref` and exception `catch` can drop fuel accounting (RUSTSEC-2026-0315, medium), and dynamic record lifting can allocate past the hostcall fuel limit (RUSTSEC-2026-0316). Both are fixed in 48.0.3. `cargo audit --deny warnings` fails the Audit workflow until the bump, and it needs no source change: the runtime, component-model and async surfaces this host uses are unchanged across 47 -> 48. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contributor
Author
|
@julian-avar Can you confirm that the fs API you're using is compatible with https://github.com/MicroPerceptron/kore/pull/1064's upgraded dagfs? I foresee we transition fs fully to be pack backed, the performance upside is too high not to. |
…them
The facade streams a read through this launch's storage ring only when it
is handed a destination of at least 256 KiB (`FACADE_MIN_BYTES`). Every
read this surface made was 4096 bytes — `KORE_LUA_BUFSIZE`, one page per
bridge crossing — so no file, however large, was ever read through a ring.
`f:read("a")` and `kore.fs.read_file` were that same page loop.
Three changes, and the sizes that decide them:
- `kore.fs.read_file` is one host call (`OP_FS_READ_FILE`) over
`Fs::read_file`, which opens, streams and closes under one pinned
version. Assembled here from positional reads it could not be: each of
those pins and releases its own version, so a concurrent writer could
leave the answer a mix of revisions, and no call was large enough to
stream besides. Bounded by `MAX_FILE_BYTES`, refused `range`.
- A read of at least `KORE_LUA_STREAM_MIN` bypasses the page buffer and
asks for the whole remainder at once. Both `f:read(n)` and `f:read("a")`
go through it; the page buffer takes over again once the remainder is
small, so both halves run in one large read. The buffer itself is not
grown — it is a fixed array in all 256 open-file slots.
- `KORE_LUA_FACADE_STREAM_MIN` and `files::FACADE_STREAM_BYTES` pin the
facade's threshold on each side of the bridge, `kore_lua_read_geometry`
reports what the engine actually compiled, and the store records the
largest destination any read asked for. A cap lowered for an unrelated
reason now fails a build or a test rather than silently costing every
read its ring.
Both positional loops move behind one `Transfer` seam, which is what makes
the rule they encode testable: a refusal answers *beside* the prefix that
already moved, never instead of it. Reaching that state needs a device
under the session, so it was previously unreachable from a host build and
untested in either direction.
With it comes the correction `Fs::read_at` now requires: a streamed read
writes into caller storage as it lands, so the acknowledged prefix bounds
what was read and says nothing about which bytes past it are still the
caller's. `TransferError::completed` and `docs/filesystem.md` say so.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Three defects on the write path, none of them pack-backed — S8's write grants have not landed upstream and nothing here streams a write. These are what the store already owns and this surface was not using. `kore.fs.write_file` was open + one positional write + close, and a positional write commits once per window: a save cost one image/flush/root/flush chain *per window*, not one for the save. It is now `OP_FS_SAVE` over `Fs::write_bytes` under `hold_commits`, which is the store's own `BeginReplace` transaction — one commit however many windows it takes. The release is the commit, so a refused release is the caller's answer even when every byte was acknowledged. The open no longer truncates. The replacement sets the whole contents, so truncating first was a wasted round trip — and it meant a save that then failed had already destroyed the previous contents. They now survive a refusal intact. `f:write` guessed at a short write. A short acknowledgement is a fact, not a refusal, and the old code turned every one of them into `out-of-storage` — a reason it did not know. It now asks again from where the store stopped, so a real refusal arrives in the store's own vocabulary on the next call. Only a store that acknowledges nothing without refusing has no reason to give, and that is the one case still spelled in C. The harness grows a short-write cap, because the store wrote everything or refused and so could not produce the state at all. `Fs::replace_file` also publishes the index for what it wrote and this does not: that verb is behind kore-dagfs's `index` feature, which pulls kore-svc-extract-worker-rust, and this crate enables only `runtime`. Turning it on is an image-size decision for the Lua target, not a side effect of this change. A save therefore leaves the index stale. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Written to answer whether the filesystem API this surface uses is compatible with the DagFS upgrade in MicroPerceptron/kore#1064 (`ft/pack-tickets`). It is — proven by syncing a scratch export to the PR head and compiling — and that was the problem: the change is additive at the `Fs` facade, so nothing broke and nothing improved either. Records the evidence, why `res/STORAGE_TRANSPORT.md` §9's S8 makes this a deadline rather than a missed win, and the slices. S-L0 through S-L3 and the write slice are implemented and marked; S-L4 (a `pack-ring`-gated witness) and S-L5 (sync `native/`, flip it on, measure on Gigatron) are not. Nothing here has run on metal, so §7's acceptance is unmet. Two claims an earlier draft got wrong are corrected in place rather than quietly dropped: `io.lines(name)` reads lines and `require` reads through a 64 KiB `Loader::read_source` buffer, so neither was ever going to reach the threshold, and the whole-file cap is spelled `range` rather than `out-of-storage` — the file is readable, and it is having all of it at once that is refused. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`kore.fs` was `io` with a different error table. Its whole vocabulary was open/read_file/write_file/remove/rename/mkdir/resize/stat/exists/list — POSIX verbs, kore spelling, and nothing of the store underneath. Meanwhile `src/fs.rs` already re-exports the entire graph vocabulary as `fs::dag` for Rust consumers, so Lua was the surface that had it worst. `Stat` carries eight facts and this surface passed on two of them. A script could not see the revision it was racing, the links keeping an object alive, the grants outstanding on it, or what its bytes were meant to be. `stat` now answers the whole record over a fixed little-endian layout whose length the C half checks before decoding a field. `entries` answers a directory's edges where `list` answers its names — each name with its target's class and subtype, so telling a native module from a plain file costs one call rather than a stat per name. The target's `ObjectId` is deliberately not in it: a service-local slot key is not a stable identity, and a script that stored one would be wrong in a way nothing would tell it. The stable one is `stat`'s, named as such. `rights` answers what this task may do at a path, in the store's own `rwns` spelling — `rights_label` printing what `rights_parse` reads — with the booleans derived from the letters rather than beside them. A second vocabulary in C would be a second authority over one set of bits. An unknown subtype is carried, never guessed: the store's rule is that a newer writer's subtype is data an older reader passes through, so `subtype` is nil where this build cannot name one and `subtype_bits` still reports it. None of this is authority. A stable id conveys none, a subtype gates nothing, and `rights` reports what a walk already found. Deriving a view, delegating one, and the grant vocabulary stay absent until MicroPerceptron/kore #986 and #991 rule the capability model for a userland authority domain; a surface invented here first would have to be retracted. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`docs/filesystem.md` documents the Rust facade and `languages.md` the C/Zig ABI; the Lua surface had only its crate's module header, which is the wrong place to look something up from a script. Organised around the thing that is actually true of it: `kore.*` is the store's vocabulary spelled for Lua, and `io`/`os` is the same code with the capabilities defaulted. So every verb is listed in both spellings, and the mode-string-to-rights mapping is stated once as a table rather than left to be inferred — including the two rights this surface never requests, `SHARE` and `BIND_EXECUTABLE`, which are the ceiling on everything a Lua script can reach. Per-verb sections carry the shapes a caller needs and the reasons where a reason exists: why `resize` takes a handle and not a path, why `entries` omits the target's object id, why an unknown subtype is absent rather than `"plain"`, why `exists` is the one verb with no failure spelling. Deviations from stock Lua are tabulated rather than scattered — `os.getenv` always nil, `os.clock` measured from VM start rather than process CPU, `os.exit` recording and unwinding, `f:flush` a durability barrier. Both error spellings and the full errno mapping are in one place. Marked aspirational, per AGENTS.md: a closing section lists what is not exposed and what blocks it — unblocked and wanted, blocked on MicroPerceptron/kore #986/#991, or blocked on the `index` feature. Storage-ring streaming is described as the shape that can carry it rather than as present behaviour, because the pinned export has no ring yet. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`kore.fs` has answered the graph since `stat`, `entries` and `rights` landed, but it still took authority in POSIX's spelling. `kore.fs.rights` prints `"rwn"`; `kore.fs.open` would not accept it. The surface reported rights in one vocabulary and accepted them in another. A mode string fuses two questions. `"w"` is write *and* create *and* truncate, which is a projection of a right set onto three behaviours. So they separate: authority is the rights spelling, behaviour is a trailing options table (`create`, `truncate`, `append`, `exclusive`). The spelling is not interpreted in C. It crosses packed into one argument word as ASCII and `kore_dagfs::rights_parse` — the parser whose output `rights_label` prints — does the mapping. A copy of that mapping in C would be a second authority over one set of bits, which is what `kore.fs.rights` already avoids by deriving its booleans from the store's own letters. `n` and `s` are refused by name. A route carrying `MUTATE_NAMESPACE` or `SHARE` is useless while no verb here can link or share, and accepting the letter would claim a reach this surface does not have. They unlock with their verbs, not before. `io.open` converges onto the same path: its mode is translated to a spelling and a set of options in C, so the capability default documented in `docs/lua.md` lives in one place rather than being derived again on the far side. Nothing about `io` changes, and its suite is the witness — it passes unaltered. The break is loud by construction. `"r"` is unchanged; `"w"` becomes a read-write route that does not create, so a missing file is `not-found`; `"a"`, `"r+"`, `"w+"` and `"rb"` raise, because `a`, `+` and `b` are not rights letters. The argument error names the syntax. `route_rights` is unit-tested directly — the host smoke reaches only the harness's model of the rule — and the harness now models a spelling it will not open a route with, so `usage` is reachable from a host build. Verified by negative control: accepting an unreachable right fails the open suite at its `'rn'` assertion. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`lua.md` is the reference and `pack-backed-reads.md` is the storage-transport plan; the arc that turns `kore.fs` from POSIX-with-kore-errors into the store's own surface had no written shape. States the rule the whole arc turns on — authority is a rights string, behaviour is an options table, and the surface never offers a right it cannot exercise — and why the third clause is what bounds the work: a letter unlocks when its verb lands, and until then it is refused by name rather than silently ignored. V1 is marked done and its break tabulated. V2 (create with a subtype, which is what a Lua installer needs to write what `require` loads) through V6 are aspirational, as is the blocked set: grant creation and projection on kore#986/#991, index publication on the `index` feature. Acceptance is unmet — nothing here has run on metal. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Three advisories landed against Wasmtime 48.0.3 on 2026-10-02: mis-typed WebAssembly tag imports can corrupt the GC heap (RUSTSEC-2026-0325, medium), rooting for GC values live across `try_call` may be missing (-0326, medium), and a component async-lifted callback's result count is unvalidated, overflowing a native stack buffer (-0327, **critical**). All three are fixed in 48.0.4; 48.0.5 is the current patch of that line. `cargo audit --deny warnings` fails the Advisories workflow until the bump, over both this lockfile and `native/`'s. It needs no source change: a patch release within 48.0.x, and the runtime, component-model and async surfaces this host uses are untouched. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`lua.md` stated the mode-to-rights convention but never showed a script using one, and never placed the Lua surfaces against the other two. A reader could not tell why `kore.fs` exists beyond nicer error tables. A DagFS route is reached through an affine `View`, and only Rust can hold one: a Lua value can be copied into a table or captured by a closure, and a C struct can be memcpy'd, so neither language can be handed something affine. Both get a generation-checked handle instead, which is why `io`, `kore.fs` and `kore-ffi` all deal in handles while `kore::fs` deals in the capability itself. That is one table, and it explains the whole shape. Then what `io` cannot say, in code rather than prose: asking for less authority on purpose, asking what the task actually holds, and taking `"w"`'s three fused decisions apart so only two are made. Plus the two things only the kore surface can see — a `revision` that moves under you, and an `id` that answers "same object" where a path answers "same name". No surface can forge authority; the store resolves every route through a generational slot. What differs is how much of the vocabulary each can say, what it costs to say it, and how early a mistake is caught. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contributor
|
@SnowCheetos Take a look if its good now |
SnowCheetos
pushed a commit
that referenced
this pull request
Oct 14, 2026
Brings in Lua 5.5 (#23, #25) and the Wasmtime 48.0.3/48.0.5 RUSTSEC bumps, so kore can pin one revision carrying both its kore-lua worker and this branch's kore-wasi changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TmEMGSFqb56Tcsvuk9PQPx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Overview
crates/lua(kore-lua): the Lua 5.5 engine, its freestanding host and thekore.*standard library, moved out of Kore'suser/apps/utils/luaso any libkore consumer can embed it. Besidekore-wasi, on the same seam.A script sees two surfaces over one implementation.
io.openandkore.fs.openreturn the same file object; one failure is rendered two ways, both derived fromFsError.io/osanswer what POSIX can ask, with the capabilities defaulted from the mode string;kore.fsanswers what the store can.docs/lua.mdis the reference.ioandosare written here, not vendored: Lua'sliolib.candloslib.care libc over POSIX and this target has neither.Capabilities
A DagFS route is reached through a
View, which is affine. Only one of the four surfaces can hold one, because only one has a language that enforces it.io/os__close/__gc, plus a generation checkkore.fsr,wtoday;n,sas their verbs landkore-ffi)KoreFsHandleuint32_t;KORE_FS_READ,KORE_FS_WRITEkore::fs)ViewitselfRightstype, andfs::dagLua and C are in the same position for the same reason: a Lua value can be copied into a table or captured by a closure, and a C struct can be memcpy'd, so neither can be handed something affine. Both get a handle — an index and a generation into a table thread 0 owns — and a stale one is
invalid-handle. Rust holds the capability itself;src/fs.rskeeps acompile_faildoctest to prove it.No surface forges authority: the store resolves every route through a generational slot. What differs is how much of the vocabulary each can say, what it costs to say it, and how early a mistake is caught.
iois not a smaller filesystem. It is the same one with the capability questions answered by convention, so it cannot ask for less, ask for more, or find out what it got:kore.fsexpresses every routeiocan, plus three things it has no spelling for: rights asked for by name, rights reported back, and behaviour chosen apart from authority.Lifetime is deterministic on both surfaces,
__gconly a backstop.revisionandidare visible on neitherionor POSIX: one moves on every commit, so a script can tell a write landed underneath it; the other is volume-stable, so it answers "same object" where a path answers "same name".docs/lua.mdhas the worked version.kore.fs
Each answers
nil, erron failure, whereerris{code, kind}with a__tostringof the code.open(path [, rights [, options]])read_file(path)""for an empty onewrite_file(path, bytes)resize(file, bytes)true; takes an open file, not a pathstat(path)entries(path)rights(path)list(path)exists(path)mkdir(path)trueremove(path)truerename(from, to)true; the store's own move, across directories, keeping identity and grantsopentakes authority and behaviour separately:rightsis a subset of the store'srwnsspelling, each letter once, parsed bykore_dagfs::rights_parserather than in C.READis implied, so"w"is a readable write route.nandsare refused by name until the verbs that use them exist.optionsarecreate,truncate,append,exclusive.Mistakes raise: a malformed spelling, an unreachable right, an unknown option key,
truncateorappendwithoutw,exclusivewithoutcreate. A refusal from the store arrives asnil, err.statanswers the object's whole record. None of it is authority.size_byteskind,directory"file"or"directory", and the same fact as a predicaterevisionidlinks,grantssubtype"plain","executable-image","package-manifest", or absent when this build cannot name the bytesubtype_bitsentriesanswers{name, kind, directory, subtype?, subtype_bits}per edge, wherelistanswers names.rightsanswers{spelling, read, write, mutate_namespace, share};spellingis what a latersharewould accept, and the booleans are derived from it.The file object
One userdata, whichever surface opened it.
f:read(...)"a","l","L","n", or a byte countf:write(...)f:lines(...)f:seek([whence [, offset]])f:flush()true; a durability barrier, not a buffer drainf:setvbuf(mode)true; accepted and inertf:close()true; idempotentf:closed()The stream position and its read-ahead live in the file object, not beside the view, so there is one authority for it; the host side is purely positional. A handle is an index and a generation, so a use after close is
invalid-handlerather than a read of whatever took the slot.__closeand__gcboth retire the handle, deterministic close first.io and os
Twenty of the twenty-two members the manual lists, and all seven
file:methods. Where the manual leaves a detail to the system, this build chooses and records it inKORE-VENDOR.md:os.getenvnilfor every name; a task's authority arrives as capabilities, not stringsos.date,os.timeisdstfalse; civil time computed, there being nogmtimeor timezone database belowos.clockos.exit<close>handlers runos.setlocale"C",nilfor anything elseos.tmpname,io.tmpfileio.tmpfileremoves its own on retireio.lines(name)Absent:
os.executeandio.popen, both of which launch a process — a service face the embedding program holds and this crate does not, so they arrive throughExtension. Alsostring.format(no hostedprintf) andload/loadfile/dofile/string.dump(no bytecode verifier upstream).The rest of kore.*
kore.timenow(),mono_ns(),wall(),wall_ns(),sleep(seconds)kore.randombytes(n)forn <= 4096,integer(lo, hi),available()kore.procargs, a lazy 1-based array fetched oncekorehas(name),caps(); an unheld module isnil, soif kore.fs thenis the idiomrequirelibs/;<name>.luaover<name>.so; source bounded at 64 KiBErrors
kindis"refused"when the service named a reason,"unavailable"when the call could not be made at all. Expected failures answernil, err; programming errors raise.The embedding API
Host::start::<E>(transport)Host::eval::<E>(services, extension, script, chunk_name, repl)require; answers(Outcome, stdout, stderr)orFatalHost::quit(self)Services::new(rt, transport, args)Services::{exit_request, close_open_files, fs}os.exitrecorded, teardown, the attached faceOutcomeOk,Failed,Exited,Incomplete,ResourceRefused,StartupLibrary::new(name, opener)registers one module beneathkore.AnswerisValue(u64, u64),Bytes(u64, Vec<u8>),Refused(String)orUnavailable(String). Ops at or aboveOP_EMBEDDER_BASE(64) reachserve; below it the vocabulary is this crate's.kore_lua::apiis the Lua C API subset an opener calls, andfiles::codeis public so an embedder's own filesystem failures render in the same words.Extensioncarries no'staticbound. It forbade the one shape an embedder has — a face borrowed from theRtand transport itsmainowns — and bought nothing; the worker reads onlyLIBRARIES.The transport grows one borrowed byte window so a path or a write's bytes cross without a copy. No new word state, and a verb is still a constant and an arm.
native/
Re-exported at Kore
9e4172bc2. The export growskore-ldandkore-late, whichrequire "<name>.so"needs to check a store object stampedEXECUTABLE_IMAGE, place it in the task's JIT arena and bind it against the exported Lua C API (Koreres/IMAGE.md§8).Not implemented
Deriving a view, delegating one, and the grant vocabulary. Everything here reports rather than confers: a
StableObjectIdconveys no authority, a subtype gates nothing,rightsreports what a walk already found. Conferring or moving authority waits on MicroPerceptron/kore#986 (grant boundary, route shape, rights vocabulary) and #991 (whether a userland authority domain gets a generic capability type).Index publication on save waits on kore-dagfs's
indexfeature, which pullskore-svc-extract-worker-rust; enabling it is an image-size decision for this target.search, grant listing,createwith a subtype and scoped commit batching are planned.docs/kore-fs-vocabulary.mdhas the order,docs/pack-backed-reads.mdthe storage-transport half.Gates
make fmt test check docs owners cross miri bindings-checkandtools/check-local.pyclean.make crosscovers x86_64, aarch64 and riscv64gc and now includes this crate, so a target-only regression in the amalgamation meets a compiler instead of a keytest.The host smoke runs the whole surface on every build against the in-memory store in
tests/host_fs.c, which models the refusal vocabulary, the graph facts, short reads and short writes. Each behavioural claim has a negative control: stubbing out the large-read bypass, the short-write retry, the unknown-subtype carry or the unreachable-rights refusal fails that claim's assertion and no other.The smoke also gets the right prototype for
kore_lua_new: it declared three parameters against a definition taking five, so every run passed two garbage registers thatluaopen_korethen walked as a library array.Not proven on metal
Kore's
user/apps/utils/luastill carries its own copy of the engine and pins libkore at861cef90, from before this crate existed, so a Kore image builds a different program. Making that app a thin embedder overkore-luais the follow-up. Until it lands, every measurement here is a host build.Wasmtime
Two bumps unrelated to the Lua work, 47 → 48.0.3 and 48.0.3 → 48.0.5, for RUSTSEC-2026-0315, -0316, -0325, -0326 and -0327. The Advisories workflow fails on all five. Neither bump needed a source change.
Authored by @julian-avar