-
Notifications
You must be signed in to change notification settings - Fork 256
Description
There are some use cases that require a host to know some information about the module that invoked it. Examples include access to secrets, or maintaining separation of data in a multi-tenant server. How the host handles a call at runtime could depend on the access rights of the user that loaded the module, or perhaps capabilities that were attached to the module itself (for example, wasmcloud attaches a JWT with signed capability claims to a custom section of the wasm module).
It's entirely possible that I've overlooked something and wit-bindgen isn't the best way to address this. To clarify intent, I'll go into a little more detail on the use case and challenge.
Suppose you want to use a KV-like WIT interface to allow webassembly modules to query a secrets store on a multi-tenant server. When the module requests access to a secret, the host must have some way to determine whether access should be allowed. If the api signature is just get(key) -> value, the host doesn't have enough information to resolve it. Given that a module's code may be "untrusted", we cannot allow parameters of the request to declare its own access rights, and we certainly don't want to compile credentials into the module. A multi-tenant server could manage access by placing a user-id in the Store at the time the module is loaded, and use that data at runtime to lookup access rights before executing the query. The mechanism used by the host for managing user ids or determining access rights is beyond the scope of the component model and WIT. However, for the host to properly execute the request from the module, it must have some trusted information about the user or account performing the request. The Store is a natural place to store that info, and it's trusted because it's not directly accessible by the module. If a reference to Store is generated as an extra parameter to the host call, the host can easily check permissions and decide how to process the request.
This issue made the same request, and was rejected because it appeared that there was a need for the host to call back into the module, in violation of the component model design. This proposal makes the same request, but the use case is slightly different. The host needs access to the Store not so that it can invoke the module, but so that it can get an identity token from the Store.
Ugly Work-arounds
There are some work-arounds to accomplish the goal, but they are much less desirable.
- Add the Store parameter by manually generating the host binding interfaces - or hand-editing the generated code. This is messy, error-prone, and difficult to maintain.
- Change all incoming interfaces and outgoing interfaces for the module to include an additional session token or context parameter. Since the user session information can be added to the Store, it can also be pulled from the Store to pass into the module with all invocations, so that it can be passed out again when the module calls the host. This is undesirable because it pollutes unrelated interfaces by adding identity or session information, complicates module implementation by requiring all methods to pass through an extra parameter, and makes the interfacess less portable by encoding information about how some particular host wants to represent sessions or identity. That goes against the principles of WIT.
Misc details:
- wasmtime
TypedFunc.calland.call_asyncrequire a mutable reference to Store. When that module in turn calls out to the host via a generated WIT binding, it is that outgoing interface that I'm proposing needs a read reference to the same Store. If the Store is accessed read-only, I don't think it violates the exclusive access guaranteed by the mut reference. - The host would have to ensure that there are no reentrant calls to the module. This is already a requirement.
- The host cannot cause the module's memory to grow. I'm not sure of the details, but this requirement may imply that the reference to Store must be read-only, or at least that it doesn't grow. For any use case I can think of where the host would want to update module-specific data structures, the host could maintain a hashtable where the immutable key is in Store, so Store could still be read-only and the hashtable in host memory can grow arbitrarily.
Thanks for considering this.