| `redis` | `RedisManager::instance()` | only when Redis was booted for this context |
| `translator` | `Translator::class` | |
| `events` | a fresh `EventDispatcher` instance | also bridged to the static facade (see [Events](#events)) |
Immediately after, **`XC_Bootstrap::assertContainerHealth()`** hard-requires that `events` is
present (plus `db`/`redis` when those were booted) and throws if not — a **fail-loud** guarantee
that later code can assume these services exist rather than null-checking each one.
> The `context`/`config`/`settings`/`servers`/`bouquets`/`categories` entries are plain data
> snapshots taken at boot, not lazy factories — they are read, not recomputed, for the life of the
> request.
---
## ServiceContainer reference
All setters are chainable (`return $this`).
| Method | Purpose |
| --- | --- |
| `set(id, value)` | Register a service. A **`Closure`** value becomes a **lazy singleton factory** (called once on first `get()`, then cached); anything else — a scalar, an object, or a `[Class, 'method']` array — is stored as a ready value. |
| `factory(id, callable)` | Register a factory that returns a **new instance on every `get()`** (no caching). |
| `register(array)` | Bulk `set()` from an `id => value` map. |
| `get(id)` | Resolve a service. Returns the cached singleton if present; otherwise runs the factory once, caches it, and applies decorators. Guards against cyclic factories and throws `CircularDependencyException`; throws `NotFoundException` for an unknown id. |
| `getOrDefault(id, default)` | Like `get()` but returns `default` instead of throwing when absent. |
| `decorate(id, decorator, priority)` | Wrap an existing service, highest priority applied last. **Forbidden** on the protected services `['db', 'settings', 'config', 'auth']` — decorating one throws. See [Module Extension Points](module-extension-points.md). |
| `tag(id, tag)` / `getTagged(tag)` | Group services under a label for batch retrieval. |
> **Tags are currently unused by core wiring.** `tag()`/`getTagged()` exist, but the boot path
> collects module contributions by `instanceof` checks over the loaded-module list (see
> [`bootAll`](#moduleloaderbootall-the-orchestrator)), **not** by tag. A source docblock still
> describes tags as the collection mechanism for subscribers/cron/routes — that describes an
> intended design, not the current code. Don't rely on tag-based collection until it is actually
> implemented.
Resolving a `[Class, 'method']` handler (used by the Router) goes **through the container**, so
handler classes are DI-resolved when registered, falling back to `new` when not.
---
## `ModuleLoader::bootAll` — the orchestrator
After `ModuleLoader::loadAll()` has discovered, filtered and **topologically sorted** the modules
(see [Module Lifecycle](module-lifecycle.md)), `bootAll()` is the single place that pushes each
module's contributions into the core registries. It checks each **sub-interface** with `instanceof`
so a module implements only the hooks it needs (`ModuleInterface` is a composite of them).
Per-module order inside `bootAll(ServiceContainer $container, ?Router $router, ?StreamPipeline $pipeline)`:
1.**Core navbar first, once** — `(new CoreNavbarProvider())->registerNavbar(...)` before any module, so core menu nodes exist as parents.
2.`ServiceProviderInterface` → `boot($container)`**then**`registerEventSubscribers()` — services are registered before that module's listeners are wired.
3.`StreamMiddlewareProviderInterface` → `registerStreamMiddleware($pipeline)` — **only if a `$pipeline` was passed**.
4.`RouteProviderInterface` → `registerRoutes($router)` — **only if a `$router` was passed** (`$router !== null`).
2.`Router::getInstance()`, then `require` the core route files `routes/{scope}.php` (+ `routes/api.php`).
3. Module boot block: `router->beginModuleRegistration()` → `new ModuleLoader; loadAll(); bootAll($container, $router)` → `router->endModuleRegistration()` → `drainRouteCollisions()`. Module-registration mode makes **core routes win** over any module route with the same path; collisions are captured, not silently overwritten.
4.`Router::dispatch()` / `dispatchApi()` handles the request, resolving `[Class, 'method']` handlers through the container.
See [Bootstrap Contexts](bootstrap-contexts.md) for exactly which subsystems each context initialises, and [HTTP Request Handling](http-request-handling.md) for the Router API and dispatch.
---
## End-to-end boot — CLI
`src/console.php`:
1.`require bootstrap.php`; `XC_Bootstrap::boot(BootContext::Cli)` — DB, `LegacyInitializer`, optional Redis, process title, then the **same**`populateContainer()` (so `events` and friends exist in CLI too).
3.`new ModuleLoader; loadAll(); registerAllCommands($registry)` (module commands, **before**`bootAll`), then `bootAll(getContainer())`**with no router and no pipeline** — so routes and stream-middleware are skipped; only module services + event subscribers wire up.
4.`registry->dispatch($argv)` runs the requested command.
---
## What lives elsewhere
To avoid duplication, the author-side and per-subsystem detail live on their own pages:
| Topic | Page |
| --- | --- |
| Which subsystems each context initialises; `boot()` options; idempotency | [Bootstrap Contexts](bootstrap-contexts.md) |