iii-worker-manager worker owns the connections between workers and the engine. It always runs
a trusted listener (on 49134 by default) that your own workers connect to through III_URL.
To let an untrusted client connect, you add an RBAC-gated listener: a second port where
every connection is authenticated by a function you write and every call is checked against an
allowlist.
iii-worker-manager is always running, so there is nothing to add; you configure it directly in
config.yaml. This page is a quick tour of RBAC listeners. For the full configuration see the
iii-worker-manager worker docs.Trusted and RBAC listeners
The default49134 listener is trusted: any worker that reaches it can register functions and
call anything. That is fine for workers you run yourself, but not for a browser or other untrusted
worker. An RBAC listener runs on its own port, gates every connection through an auth function,
and restricts each session to an allowlist of functions.
Declare one iii-worker-manager entry for each listener:
config.yaml
iii-browser-sdk; see
Ch. 7: Bring in the browser.
auth_function_id names a function the worker invokes once per connection to authorize the worker
to connect (or reject it if authorization fails). That is covered in the next section.
expose_functions is the allowlist of function IDs a session may call. Each entry is either a glob
match("...") on the function ID or a metadata: selector that matches any function registered
with that metadata:
Namespace-scoped function rules
Anexpose_functions rule without a namespace applies to default only. Add namespace when the
session must call a function in another namespace:
match value supports * wildcards for the function id. The namespace value is an exact
match and does not support wildcards. Add one rule for each namespace that the session can access.
A function’s metadata is an arbitrary JSON object set when the
function is registered, so you can shape it however your access model needs and gate on exactly the
fields you choose, for example { public: true }, { tier: "premium" }, or { scopes: ["read"] }.
auth_function_id is connection authorization, and it is not deprecated. The deprecated
auth_function setting belongs to iii-stream, which is a separate worker.Write the auth function
The auth function runs during the WebSocket upgrade, before the client is connected. It receives the request’s headers, query parameters, and client IP, and returns the session’s permissions. Throw to reject the connection.The auth function can grant multiple authorizations so a single function can be used to authorize
multiple types of workers. In practice this is a balance between authorizing similar workers and
separating dissimilar workers to separate
iii-worker-manager definitions. You can define any
number of iii-worker-managers in iii’s configuration.- Node / TypeScript
- Python
- Rust
- Go
src/index.ts
49134 port; it is one of your own workers. Only
the untrusted client connects through 3110.
The returned object is an AuthResult:
Scope a session to namespaces
namespaces maps a namespace to the function IDs the session may call inside it. An entry accepts
an exact ID or a wildcard, in either the bare or the match("...") spelling expose_functions
uses:
engine::workers::register and address on a call. A session holding this result registers in
browser or linkly; declaring default, or any other namespace, is refused with FORBIDDEN, and
a call into a namespace outside the list is refused with FORBIDDEN. An entry with an empty list,
like linkly above, grants the namespace and no functions inside it: the session registers there
and reaches only what expose_functions exposes.
Return an empty namespaces (or omit it) to leave the session unscoped: it declares any namespace,
and only allowed_functions and expose_functions apply.
Intercept calls with middleware
Setmiddleware_function_id on the listener to run a function on every invocation that comes
through the RBAC port. It receives the call plus the session context from the auth result, and
acts as a proxy: forward the (optionally rewritten) call and return its result, or throw to reject.
config.yaml
- Node / TypeScript
- Python
- Rust
- Go
src/index.ts
rbac block also accepts an on_function_registration_function_id hook. It runs when an
untrusted session registers a function, and can remap the registration (return the mapped fields) or
throw to deny it. Its input includes the function’s args_format, private and observable
options, and its answer may rewrite them.
on_observer_registration_function_id does the same for observers. It
receives
{ observer_id, function_id, namespace, args, filters, target_function_id, target_namespace, metadata, context }.
An object answer may rewrite function_id, target_function_id, args, filters, and metadata;
any other answer, or an error, denies the observer with FORBIDDEN. Independently of the hook, a
session may observe a function only when its grants (expose_functions, namespaces) cover both
the observed function and the target.
A worker that reconnects and reattaches keeps its observers only as far as its new session allows:
the engine checks the grants again and calls the hook again for each observer, with the registration
as the worker asked for it and the new session’s context. An observer either check refuses is
disposed with FORBIDDEN. An object answer keeps the observer, with the fields it names rewritten
and the others left as the worker asked for them. Any other answer, or an error, disposes the
observer with FORBIDDEN.
A private function accepts calls only from the
connection that hosts it. A call to it from any other session fails with FORBIDDEN, whatever the
session’s grants, and an observe that names it as the target fails with FORBIDDEN. A session
whose grants cover a private function may observe it.
Calls a worker makes to its own functions normally run in-process
(local dispatch). For a session with a
middleware_function_id, the engine turns local dispatch off, so every call still passes through
the middleware, except calls of a function the session hosts privately: those skip the middleware
and still run in-process, because the middleware’s forward would come from another connection, which
a private function refuses.