Open sebastianwessel opened 4 months ago
Can I also inject javascript objects from the main process into the QuickJS runtime? I'm building a node express based data server, where I want to allow clients to run javascript queries on that data (https://github.com/SimplyEdit/SimplyStore/)
Hey @poef
Yes, you can. See Data Exchange Between Host and Guest
You can use env
, and provide strings, numbers, arrays, objects and functions.
const { evalCode } = await createRuntime({
env: {
MY_PROCESS_ENV: 'some environment variable provided by the host',
KV: {
set: (key: string, value: string) => keyValueStoreOnHost.set(key, value),
get: (key: string) => keyValueStoreOnHost.get(key),
},
},
})
Read standard input to V8's d8
(/proc/PID/fd/0
) with WebAssembly. Right now I'm using QuickJS via os.system()
https://github.com/guest271314/native-messaging-d8/blob/quickjs-stdin-read/nm_d8.js#L16, https://github.com/guest271314/native-messaging-d8/blob/quickjs-stdin-read/read_d8_stdin.js. If we can do this with WebAssembly we can get rid of os.system()
which call sh
.
@guest271314 thanks for your feedback. As far as I understand, if the "regular" deadline nodejs module is available inside of the quickjs runtime, it should solve the issue or? So, the code inside the sandbox would look like this
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
terminal: false
});
rl.on('line', (line) => {
console.log(line);
});
rl.once('close', () => {
// end of input
});
As far as I understand, if the "regular" deadline nodejs module is available inside of the quickjs runtime
Where does Node.js API's get in to your WASM QuickJS build?
The idea is to use the least amount of resources to read d8
shell standard input. QuickJS (quickjs-ng) is around 1.2 MB.
I was thinking I could use WebAssembly/WASI to read stdin to d8
using WebAssembly.compile()
.
I'm basically trying to do this https://github.com/guest271314/native-messaging-webassembly without a WASM runtime, using the built-in WebAssembly
object alone - within d8
. I created a different solution for Mozilla SpiderMonkey.
What I was meaning is, that you can provide functions and data from a regular Node.js host application, to the wasm guest system. Basically, you would read the standard-in in the host application and provide this data than to the guest system, as a copy. What you try is, to disable the isolation of the wasm, and to allow to access the host system directly. Kind of disable security and enhance WASI in the direction of wasix. In this project, this not wanted, as the JavaScript should run in an isolated sandbox, ensuring that it is not possible to break out, and access host functionality directly, in an uncontrolled manner.
What I was meaning is, that you can provide functions and data from a regular Node.js host application, to the wasm guest system.
It doesn't make sense to me to use 108.7 MB node
executable that depends on V8 to read standard input to v8
at 37.9 MB.
I use deno
and bun
and qjs
and tjs
the same that I use node
, so I don't think of node
as a "regular Node.js application". node
is just another JavaScript runtime in the JavaScript toolbox for me.
That's why I chose qjs
at 1.2 MB to do the task.
I have been trying to do this using d8
s readline()
, though have not succeeded, yet.
I saw your work and this issue requesting features and decided to place a feature request.
In this project, this not wanted, as the JavaScript should run in an isolated sandbox, ensuring that it is not possible to break out, and access host functionality directly, in an uncontrolled manner.
I don't think it is possible to achieve that requirement. I have broken out of too many alleged "sandbox" to think for a moment that it can't be done in this case, too.
Thanks!
Hey, No worries, it's totally wanted to place such issues - even if I can't help here. Can ask you, what's the general use case you like to achieve?
I don't have a use case for running applications in a "sandbox".
I generally break out of sandboxes that folks try to set up.
For people interested in "sandbox" code we already have that with Worker
and WebAssembly in and out of the browser and SharedWorker
, Worklet
interfaces in the browser.
Set Limits Maximum Memory Max CPU Time - as a decimal between 0-1 1 being its allowed to consume 100% of the core/process that it is running on.
Observability It should be possible to poll the sandbox to find out, how much memory is being consumed (either as a value or as a percentage), same as CPU time.
Informative Errors
If the sandbox is destroyed because it exceeds the max memory or the cpu is used too much there should be a clean informative error thrown (according to the new design it might be good to throw this at both the runtime.runSandboxed
level as well as at the sandbox.evalCode
level.
I don't think its possible to include anything at present like the --allow-net feature of deno in node. However instead of marshaling (to and from) a fetch replacement which imposes this limitation within javascript. It would be desirable to look towards seeing if there is any way possible to impose a limitation on the sandbox runtime. If it was a separate process, there are ways to do this from the OS. But I have not found out a compatible solution for worker threads. However if there was any possible way to do this, it would be very good.
Hey @digipigeon thanks for your feedback. There was a similar question recently Limit CPU and memory usage. The idea of polling from outside is interesting, but there is no simple working solution for it in node. As long as the eval function is running, the host side is kind of blocked. If there is no eval executed, it is already possible to get memory information. As the intention is, to have a highly controlled and isolated sandbox, even if it would be technically possible to allow direct net access, I don't think I will add it (at least per default). The current focus is, to provide an environment, which is as close as possible to node and similar runtimes.
Hi @sebastianwessel, would you consider implementing https://nodejs.org/api/worker_threads.html#performanceeventlooputilizationutilization1-utilization2 as an alternative to accomplish something similar?
@digipigeon Not directly in this library, as the focus is on providing a sandbox, data (de-)serialization, runtime compatibility inside the sandbox, and developer experience (DX).
The developer should be free to choose the method that fits best. My recommendation is to use libraries that are specialized for this, such as the poolifier-web-worker package, which is used in the Server Example here.
For instance, the usage in the browser might differ from that in the backend. In the browser, you might need only one sandbox, while in the backend, as many sandboxes as possible are required.
Can there be fuel metering
like in wasmtime?
Since it's similar concept in terms of running JS in an isolated environment using some engine compiled to WASM
@aashutoshrathi as quickly is used, there is the option to do something like this:
‘‘‘typescript let interruptCycles = 0 runtime.setInterruptHandler(() => ++interruptCycles > 1024)
‘‘‘
@sebastianwessel and I can use interruptCycles
as fuel here?
Kind of - it highly depends on your use case and what you like to achieve I guess.
Personally, I do not see any real world use case, where it makes sense, to count such things, because in this case, you will need up front what is executed in the sandbox, to find the correct value. It is simply the wrong layer to control such things imo. When it comes to resource consumption, you probably would need to do it on the webassembly level. Meaning you would need to configure node/bun/browser to restrict the wasm resources.
If you have ideas, wishes, feature request and feedback - please add them in the comments below