Skip to main content

What a namespace is

A namespace is a routing value that the engine stores with a function id and a worker name. It is not part of either value. For example state::get can exist as the same function id in default, orders, and analytics namespaces. The engine uses these registry keys:
  • (namespace, function_id) for functions.
  • (namespace, worker_name) for workers.
  • (namespace, trigger_type_id) for trigger type providers.
A worker connection has one namespace. Its function, service, and trigger type registrations use that namespace.

Trigger types and the two namespaces a trigger names

A trigger names two namespaces, and they answer different questions:
  • namespace is where the target function resolves when the trigger fires.
  • trigger_namespace is where the trigger type’s provider is found.
Both are optional. namespace absent means the engine’s default namespace. trigger_namespace absent is not default: the engine resolves it, taking the registering worker’s namespace first and the default namespace second. That order is what lets a project ship its own provider for a trigger type the engine also provides. The engine’s own providers (http, cron, state, stream) live in the default namespace, so a worker that names nothing reaches them. A project that registers its own provider for the same type id gets that one instead, without any worker changing how it binds. Naming trigger_namespace explicitly is strict: that namespace or nothing. A binding that names one is never moved. A provider that registers after a binding already fell back to the default namespace claims that binding back. Start order therefore does not decide which provider serves a project.

What namespaces enable

Namespaces let you ship one reusable worker package and deploy it for more than one tenant on the same engine. Each tenant can run a worker named state and expose state::get. The worker package does not need tenant-specific names.
Without namespaces, the two deployments claim the same worker name and function id. The engine cannot route a call to the correct tenant. With namespaces, the tenant namespace selects the deployment.

Why the namespace is not a prefix

iii does not change state::get to orders/state::get. A function id is part of the worker contract. It appears in schemas, skills, console output, commands, and documentation. A deployment value must not change that contract. The worker author defines the function id. The operator selects the namespace when they deploy the worker. These two values stay separate.

Why routing is strict

A call with namespace: "orders" resolves in orders only. A call with no namespace resolves in the namespace of the calling worker. The engine does not search other namespaces after a miss. A fallback could send a call to another tenant. Instead, the engine returns function_not_found and lists the namespaces where the function id exists. The discovery functions are less strict when no namespace is present. For engine::functions::info and engine::workers::info, a default entry has priority. If there is no default entry, a name that exists in only one namespace resolves there. A name that exists in multiple namespaces returns an ambiguity. An explicit namespace always uses strict resolution.

Why the engine rejects a collision

Only one live worker can own a worker name in a namespace. Only one live worker can own a function id in a namespace. The engine rejects a second owner instead of replacing the current owner. A worker-name conflict is fatal. The engine closes the new connection because the worker cannot use its declared identity. A function-id conflict rejects only that function. The worker stays connected and serves its other functions. Worker registration and function registration are separate operations. A connected worker does not confirm that all its functions are registered. The SDK reports a function conflict as a warning. A restart does not conflict with its own connection after teardown starts. The new connection can reclaim the same worker name.

How a connection gets its namespace

A worker declares its namespace in the engine::workers::register call. The engine starts a namespace timer when the WebSocket connection opens. The default timeout is 5000 ms. A client can send registration messages before engine::workers::register. The engine holds RegisterFunction, UnregisterFunction, RegisterService, RegisterTrigger, and UnregisterTrigger messages until it knows the namespace. It does not register them in default and move them later. If engine::workers::register arrives first, the engine sets the declared namespace. An absent value selects default. The engine then processes the held messages in arrival order. If the timer expires first, the engine sets the connection namespace to default and processes the held messages. A later worker registration cannot change that namespace. To change the timeout, use registration_namespace_grace_ms or III_NAMESPACE_GRACE_MS. See Registration namespace timeout.

Use namespaces

For deployment configuration, SDK examples, cross-namespace calls, trigger targets, discovery, and registration errors, see Use namespaces.