> ## Documentation Index
> Fetch the complete documentation index at: https://iii.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Namespaces

> iii routes functions by namespace so multiple tenants, agents, projects, and teams can isolate their work from each other when they're using the same workers.

## 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.

```text theme={"theme":{"light":"catppuccin-latte","dark":"dark-plus"}}
orders   ──► state ──► state::get
analytics ─► state ──► state::get
```

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](../using-iii/configuration#registration-namespace-timeout).

## Use namespaces

For deployment configuration, SDK examples, cross-namespace calls, trigger targets, discovery, and
registration errors, see [Use namespaces](../using-iii/namespaces).

## Related

* [Use namespaces](../using-iii/namespaces)
* [Engine protocol](../reference/engine-protocol#namespaces)
* [Upgrade from 0.22.x](../upgrading/from-0-22-x)
