Host functions
Learn how sandboxed code calls functions in the host application.
Host functions
Host functions are functions that guest code is allowed to call. They are the only supported way for source running in the sandbox to interact with your application or the systems connected to it.
A host function should expose one narrow capability. Rather than handing the guest a database client, expose a function that loads one kind of record, after the application's validation and authorization rules have run.
Defining host functions
Host functions are defined under a namespace. The namespace becomes a global object in the sandbox, and each host function becomes a method on that object.
import { run } from 'run';
const result = await run({
source: `
const user = await users.find("user_123");
return user;
`,
hostFunctions: {
users: {
find: async (id: string) => {
return { id, name: 'Ada' };
},
},
},
});The fully qualified name of the host function in this example is users.find.
The users namespace is available only for this invocation.
A namespace must be a valid JavaScript identifier, must not conflict with a
reserved global, and must not begin with __run.
Calls
Calling a host function creates a request from the guest to the host. The arguments are serialized, copied to the host, and passed to the host function as ordinary function arguments. The returned value is then serialized and copied back to the guest.
Host functions may return values directly or return promises:
const hostFunctions = {
math: {
double: (value: number) => value * 2,
},
users: {
find: async (id: string) => {
return { id, name: 'Ada' };
},
},
};Guest code should await every host function call. Returning while host function work is still pending can fail the invocation because the runtime cannot safely detach host work from the guest that requested it.
await events.record({ name: 'opened' });
return { recorded: true };Several host function calls can run concurrently when the guest awaits them together:
const [profile, orders] = await Promise.all([
users.profile(userId),
orders.list(userId),
]);The invocation limits both the total number of host function calls and the number that may be in flight at once.
Context
A host function can call getHostFunctionContext() to read information about
its active request:
import { getHostFunctionContext } from 'run';
const hostFunctions = {
users: {
find: async (id: string) => {
const context = getHostFunctionContext();
return {
id,
logicalRunId: context.logicalRunId,
name: 'Ada',
};
},
},
};getHostFunctionContext() exposes the following information and controls:
abortSignallets the host function stop its work when the run is cancelled, times out, or fails.invocationIdidentifies the current execution attempt. It changes when an interrupted run is replayed.logicalRunIdidentifies the complete workflow and remains stable across replay attempts.requestIdidentifies the current host function call within the invocation.requestIndexcontains the one-based order of the host function call.hostFunctionNamecontains the fully qualified host function name, such asusers.find.interrupt(payload)pauses the run and returns an application-defined payload to the host.resumeis available when an interrupted host function is invoked again. It contains the interruption ID, original payload, and supplied resolution.
Use these identifiers to trace, audit, and deduplicate external side effects. They are not authentication credentials.
Cancellation
The host function context includes an AbortSignal. It becomes aborted when
the invocation is cancelled, times out, or otherwise fails.
Host functions should pass this signal to operations that support cancellation:
const hostFunctions = {
search: {
query: async (query: string) => {
const { abortSignal } = getHostFunctionContext();
return searchClient.query(query, {
signal: abortSignal,
});
},
},
};Aborting the signal cannot automatically undo work that has already completed. Host functions that perform writes should use application-level idempotency and deduplication.
Failures
When a host function throws, its guest-side promise rejects. Guest code may catch the error and continue:
try {
return await inventory.reserve('item_123');
} catch (error) {
return {
reserved: false,
message: error.message,
};
}The guest receives a sanitized error without the host stack or private diagnostic details. The host should record the complete error before rethrowing it when those diagnostics are needed.
Host functions are trusted code. They should validate every argument, enforce authorization in the host, and return only the data required by the source.