Repository navigation
wasi-python 0.1.0a3
Pre-release
Pre-release
·
1156 commits
to main
since this release
- Breaking: nothing is writable by default (the owner's rule, constitution §4.15; spec 025).
A call given no root and no grants has no/, no/tmpand noHOME: it can import,
compute, print, read the stdlib and call the door, and nothing else. Before, it got a fresh
private writable/. To keep that, passroot=True(--private-rooton the command line);
or grant the folders the program needs.root=Nonenow means no root, likeFalse. - Security: a
rootis prepared only through the folder that was checked (the 0.1.0a3
security review, S1). The runner maderoot/usr/local/libandroot/tmpby path after
checking the root, so a guest of another call with a writable folder above it could swap that
folder for a link in between, and the runner removed a file namedusr,local,libor
tmp, or made folders, outside every grant. It now works through the walked folder's
descriptor; the guest never saw the wrong folder before, and still does not. A writable grant
at, inside or above the host Python's ownstdliborsite-packagesis now refused (S2). The
patch applier refuses a source tree that holds.git(S3). np.saveandnp.loadof files work (the first carried patch,
recipes/patches/numpy-2.5.2/0001-npy-files-without-dup.patch): they raisedOSError58,
because numpy'stofile/fromfileneedos.dup, which WASI lacks. The patch is the fix numpy
is asked to take (its pull request is drafted for the owner) and goes when numpy releases it.
ndarray.tofile,np.fromfileon real files and memory maps still fail. The next §5.6 security
review reads this patch.- Carried patches (the owner's decision; constitution §4.1): a fix the project cannot wait
for may now be carried as a patch to a pinned project's source, underrecipes/patches/, with
its upstream pull request drafted undercontext/upstream/patches/. Each is reviewed, hashed in
MANIFEST, named here, and deleted once upstream releases the fix. None is carried yet; the
release check is now "the source trees are their tarballs plus the recorded patches". - Typed grants:
run(grants=[Grant(host, guest, writable=False), ...])gives the guest
more host folders, each read-only unless it sayswritable=True, nested in any order;
root=Falsegives it no/, only its grants. Every grant androotis walked folder by folder
and checked before any work: no link a guest could have made, no read-only grant inside a
writable one, no writable grant at, inside or above the runtime's own folders. After wasmtime
opens each folder, the runner checks it opened the one it checked.HOMEis made in the folder
the guest sees at/tmp, and there is none without one. - The stdlib zip is read-only and no longer copied into a root: every call sees it read-only
at/usr/local/lib, from one folder per process that its exit removes (a killed process's is
swept like its other folders). Apython315.zipthat 0.1.0a2 left in a reused root is never
read again and may be deleted. - Some
root=values are now refused (a change): a root at, inside or above the runtime's
own folders, such as the home folder, a project folder holding.venvorbuild/artifacts,
or the temporary folder itself, since its guest could change files later calls trust; a root
holdingwasi_python's or wasmtime-py's own code; a root still to be made where the module
cache will be made; and a root reached through a link in a folder you can write. A missing
root is still made. No grant, read-only either, may hold the temporary folder, which holds
every call's private folders andstdin. - Grants on the command line:
wasi-python run --grant GUEST=HOST,--grant-writable GUEST=HOST,--root HOSTand--no-root, each one ofrun's arguments. wasi-python run: a command that runs one program and prints its result as one line of
JSON, so any language can run agent code by spawning it (python -m wasi_python runis the
same). Each option is one ofrun's arguments; without options the call hasrun's defaults.- A killed process's files are cleaned up: a process killed mid-call left its private root
(the guest's files) and its stdin folder (the caller'sstdin) in the temporary folder for
good. Each now has a locked lock file beside it naming the kernel that made it; the next
wasi_python process on that kernel removes them at its first call, and never touches a live
call's folders or another kernel's. On a temporary folder without file locks, calls go on
and such folders stay, as before. trappednames a trap by its kind (a change to what it says): a stack overflow now reads
"stack overflow", not wasmtime's heading"error while executing at wasm backtrace:", and so
do"unreachable","memory out of bounds"and every other trap with a code. An error with no
trap code keeps its first line.- The released wheel is the bytes a second host rebuilt: the release workflow rebuilds the
wheel on arm64 Linux and publishes only a wheel equal to it, byte for byte, whose artifacts
match the set'sSHA256SUMS. - Run from async code:
run_asyncruns a call on a worker thread, so an async server's
event loop never waits for a guest. Cancelling the awaiting task stops the guest (checked
every 50 ms while it computes, at once when it waits or calls the door, later when other
threads hold the GIL) and frees its worker beforeCancelledErroris raised. Calls share one
pool per process ofDEFAULTS.async_workersthreads (the CPU count, at most 8).
run(cancel=event)stops a guest from any thread (trapped == "cancelled"). A process forked
after it ran a guest now refuses to run guests, instead of aborting (macOS) or losing its
deadlines; at exit,run_async's guests are stopped and its queued calls dropped. - sqlite3, bz2 and lzma are built in, from three new pins (SQLite 3.54.1, bzip2 1.0.8,
liblzma from xz 5.8.4), with no upstream patch.bz2,lzmaandtarfile'sbz2andxz
modes write the same bytes as native;sqlite3gives native's results, except the last digits
of its math functions (trigonometric, hyperbolic,atan2,log10, one-argumentlog), which
follow the guest's C library, and what changed since native's SQLite 3.53.1, such as JSON's
float text; it has the native feature set (FTS3/4/5, R-tree, Geopoly, math functions,
dbstat, percentile).shelveanddbmnow createdbm.sqlite3files, as native does.-V
now says "no ctypes, mmap, sockets, ssl or zstd":compression.zstdis the compression module
still missing. - SQLite's limits in the guest (the README's sqlite3 in the guest): one connection holds
the database, through a<db>.lockdirectory beside it, while it writes or has a statement
open, so a second connection gets "database is locked" after its busy timeout (5 s by
default);journal_mode=walstaysdeleteunlesslocking_mode=exclusive; a guest stopped
inside a transaction (killed, out of fuel, past its deadline, or byos._exit) leaves the
lock and a hot journal in a reused root for the embedder to remove; SQLite's own
random(),randomblob()and temporary names are seeded from the clock's second, so use
secrets,uuid.uuid4()oros.urandomfor IDs; a database in a read-only directory opens
only through a URI that takes no lock, such asfile:<path>?immutable=1; no
load_extension;sqlite3.threadsafetyis 0. - Sizes: the three libraries add about 2 MB to
python.wasm(SQLite is most of it); the zip
is unchanged.import bz2andimport lzmacost nothing more on the snapshot, which already
holds them;import sqlite3is not pre-imported.
Verify a download (gh logged in; see the README's Verifying a download):
gh attestation verify <asset> --repo ClayGendron/wasi_python \
--signer-workflow ClayGendron/wasi_python/.github/workflows/release.yml@refs/tags/v0.1.0a3 \
--source-ref refs/tags/v0.1.0a3 --deny-self-hosted-runners