A language shouldn't advertise itself as "embeddable" if it does this. It means you can't have multiple interpreters, you can't use it on multiple threads, etc. GNU Guile does this too, and it's a baffling decision! For my field (audio plugins like VSTs), it means it's absolutely a no-go, because hosts can load any number of instances of your plugins and potentially run them in parallel in the same address space, they can't rely on global state like this. Each interpreter has to be separate.
Lua does this right, as does Python (as of 3.12, when they made the GIL local to each interpreter) and I think most of the JavaScript engines. And it's not hard, instead of a global `janet_init()`, just have an opaque pointer bundle all the state, like `janet_init(interpreter)`. If you want a global interpreter, just stick it in a global variable.
[1] official docs: https://janet-lang.org/capi/embedding.html
I'd also go take a look at the actual docs and code, I'm not sure I know the exact answer, but assumptions won't help
Edit: there was someone on the Zulip that mentioned working on audio plugins, and there are a couple other audio-related projects you could check out. Someone there might have a better answer -- https://janet.zulipchat.com/
Consider the most famous embedded language, JavaScript in browsers: you can have any number of tabs open at the same time, and if the JavaScript interpreters for each of those used a bunch of thread-local storage, it would place huge restrictions on how the browsers could schedule and parallelize the callbacks for the JavaScript in those tabs.
The only way I can see this working is if you spin up a thread for each instance, and when these events come in, you wake up those threads, send over the event information, block until the interpreter thread finishes. But that's both inefficient and a real architectural hassle.
All I want is an object that's like `janet_interpreter *interpreter = janet_make_interpreter();` and then you pass that to the functions instead of doing all these magic things with global variables and thread local state. That's it.
Because of this plethora of standards, most plugin developers (including us, I work at XLN Audio) use the JUCE C++ framework, which provides a uniform interface to all these formats and more. It's available on GitHub under a GPL license if you just want to play with it (it has excellent tutorials and example projects). If you're just curious about development thing, I recommend using Reaper as a host to test in, both because it's essentially free (it's free like WinRAR is free), and it has tons of options for how to run plugins (all in the main process, all in a bridged process, every plugin in a dedicated process, etc.).
Audio plugins are essentially dynamic libraries loaded at runtime, and a common way to run them (used to be universal, but some hosts are changing) is that the dynamic library is just loaded in the main process address space and the host communicates with it by calling functions on it. That means that if a single plugin (out of maybe dozens in a project) crashes, it takes the entire host down. In addition, if you have multiple instances of the same plugin (very common, you might have the same effect on multiple tracks, for instance), all global and thread_local variables are shared between them, which makes global variables a total nightmare, and raises the thread_local problem I mentioned earlier.
Our products use JUCE for the unified interface, but then we have an entirely custom Lua codebase for the GUIs and scripting the products themselves (with lots of connections to the audio engine, which is of course C++). There are very limited languages you can embed this way, because of the requirements I mentioned above. The ones I've looked at which you could possibly do it with is Lua, Python (3.12+), JavaScript and Tcl. I haven't done a lot of testing outside of Lua though, this is just me looking at the embedding APIs. You could also do it with web views, and recent versions of JUCE provides nice ways to do that.
I wouldn't do it with Janet if it uses thread local state this way, but maybe it works. It would definitely work if you spun up dedicated threads for each instance and communicated the events back and forth, but that seems like a bad idea.
Let me know if you have more questions, this is a very weird field of programming that most developers are not exposed to.