# Samples & Deployment Patterns

The Trax samples demonstrate a consistent architectural pattern: **put your trains in a library, then wrap them with thin executables**. Each executable is just a `Program.cs` that picks which Trax capabilities to wire up. The trains themselves stay deployment-agnostic. The same library powers a standalone scheduler, a GraphQL API, a distributed worker fleet, or all three at once.

This mirrors the ladder philosophy. You only add the packages you need, and you only build the executables you need. The trains don't change.

## The Trains Library Pattern

Every sample follows the same two-layer split:

```
MyApp/                          ← library (class library, not executable)
  Trains/
    Feature1/
      IFeature1Train.cs
      Feature1Train.cs
      Feature1Input.cs
      Junctions/
    Feature2/
      ...
  ManifestNames.cs

MyApp.Scheduler/                ← executable (thin wrapper)
  Program.cs
  appsettings.json

MyApp.Api/                      ← executable (thin wrapper)
  Program.cs
  appsettings.json
```

### The Library

The library project contains everything that defines *what your application does*:

- **Trains** - `ServiceTrain<TIn, TOut>` implementations with their junctions
- **Interfaces** - `IServiceTrain<TIn, TOut>` contracts for each train
- **Inputs and outputs** - POCOs that define each train's data contract
- **ManifestNames** - string constants for scheduler manifest IDs
- **Domain types** - any shared models, enums, or utilities

The library references `Trax.Effect`, `Trax.Mediator`, and `Trax.Scheduler` (or whatever layers your trains need), but it does **not** reference infrastructure packages like `Trax.Dashboard`, `Trax.Api.GraphQL`, or the effect providers. It has no `Program.cs` and no `appsettings.json`.

```xml
<!-- Library .csproj -->
<Project Sdk="Microsoft.NET.Sdk">
  <ItemGroup>
    <FrameworkReference Include="Microsoft.AspNetCore.App" />
  </ItemGroup>
  <ItemGroup>
    <PackageReference Include="Trax.Effect" Version="1.57.4" />
    <PackageReference Include="Trax.Effect.Data.Postgres" Version="1.57.4" />
    <PackageReference Include="Trax.Mediator" Version="1.23.3" />
    <PackageReference Include="Trax.Scheduler" Version="1.34.2" />
  </ItemGroup>
</Project>
```

### The Executables

Each executable is a `Microsoft.NET.Sdk.Web` project with a `ProjectReference` to the library. Its `Program.cs` calls `AddTrax()` and configures whichever capabilities this process needs - scheduling, dashboard, GraphQL, worker polling, or any combination.

```xml
<!-- Executable .csproj -->
<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
  </PropertyGroup>
  <ItemGroup>
    <ProjectReference Include="..\MyApp\MyApp.csproj" />
  </ItemGroup>
  <ItemGroup>
    <PackageReference Include="Trax.Effect.Provider.Json" Version="1.57.4" />
    <PackageReference Include="Trax.Effect.Provider.Parameter" Version="1.57.4" />
    <PackageReference Include="Trax.Effect.JunctionProvider.Progress" Version="1.57.4" />
    <PackageReference Include="Trax.Dashboard" Version="1.16.0" />
  </ItemGroup>
</Project>
```

The versions are the releases these docs are checked against. Pin exact versions, ideally once for
the solution in `Directory.Packages.props`: a floating `Version="1.*"` restores whatever was
published last, and a Trax minor release can change an API your trains call.

The key line in `Program.cs` is the assembly scan - it points at the library so the train bus discovers all your trains:

```csharp
builder.Services.AddTrax(trax => trax
    .AddEffects(effects => effects
        // ... add whatever this executable needs
    )
    .AddMediator(typeof(ManifestNames).Assembly, ...)
);
```

Different executables add different capabilities on top of the same trains. That's the entire pattern.

## The Data Layer Pattern

Samples that own relational data follow a strict rule: **one project, one PostgreSQL schema, one DbContext** (1:1:1). The `Bookworm` sample is the reference implementation, with a `catalog` domain (books, authors) and a `lending` domain (members, loans) in separate projects, each owning its own schema.

A domain context derives a shared base, `DomainDataContext<TSelf>`, which applies the default schema (on PostgreSQL), a UTC datetime converter, and seals `OnModelCreating` so the conventions cannot be skipped:

```csharp
public class CatalogDbContext(DbContextOptions<CatalogDbContext> options)
    : DomainDataContext<CatalogDbContext>(options), ICatalogDbContext
{
    public DbSet<Book> Books => Set<Book>();
    public DbSet<Author> Authors => Set<Author>();

    protected override string Schema => CatalogSchema.Name; // "catalog"

    protected override void ConfigureModel(ModelBuilder modelBuilder) { /* keys, indexes, relationships */ }
}
```

Each context ships a companion `I{Name}DbContext` interface; application code (junctions, services) depends on the interface, never the concrete type. Registration goes through `AddDomainDataContext<TInterface, TContext>` (from Trax.Effect.Data), which uses a pooled context factory plus a scoped resolver.

### Crossing schema boundaries

A domain never references another domain. A loan's reference to a catalog book is a plain integer column, not an EF navigation:

```csharp
[Table("loans")]
public class Loan
{
    [Column("book_id")]
    public int BookId { get; set; } // points at catalog.books; resolved cross-schema in GraphQL
}
```

The GraphQL `loan.book` field is resolved by a **batched cross-schema data loader** that lives in its own project (`Bookworm.CrossSchema`), the one place allowed to reference more than one domain context. EF Core cannot JOIN across two contexts, so the loader collects every requested book id in a request and issues a single `WHERE id IN (...)` against the catalog context, avoiding an N+1:

```csharp
[ExtendObjectType(typeof(Loan))]
public sealed class LoanToBookEdge
{
    public async Task<Book?> GetBook(
        [Parent] Loan loan,
        CrossSchemaLoader<CatalogDbContext, Book> books,
        CancellationToken ct
    ) => await books.LoadAsync(loan.BookId, ct);
}
```

Edges are declared in a single manifest (`CrossSchemaEdges.All`) that meta-tests reflect over to verify each edge has a real integer foreign key, a target owned by the declared context, and a registered loader.

When a context genuinely needs to read another schema's table at the EF level (rather than only at the GraphQL layer), the foreign entity exposes a static `OnCrossSchemaModelCreating(ModelBuilder, string schema)` that pins it to the foreign schema and **ignores every navigation**, so EF Core never walks the foreign model graph into the consuming context. The entity is exposed there through a scalar-only `I{Entity}Reference` interface via explicit interface implementation, which keeps it out of GraphQL discovery (discovery only enumerates public `DbSet<T>` properties), so the owning domain stays the single GraphQL owner.

### Guarding the pattern

The conventions above are enforced by meta-tests so they survive future changes. `Trax.Samples.Tests.Meta` scans source on disk (every domain context derives the base and has a companion interface, every `OnCrossSchemaModelCreating` has the standard signature, cross-schema edge resolvers live only in a `*.CrossSchema` project and always go through the loader). `Trax.Samples.Tests.Reflection` references the built assemblies and checks what only the EF model and type graph can prove (each context owns a distinct non-null schema, every edge in the manifest maps to a real foreign key and a registered loader, every train has its `I{Name}Train` interface). Allowlists carry a justification per entry and fail when they go stale.

## Deployment Models

The sample directories show six deployment topologies, each built on the same pattern.

### Model 1: Standalone Scheduler

**Sample:** `DataPipeline/`

```
Trax.Samples.Flowthru.Spaceflights/           ← library (trains)
Trax.Samples.Flowthru.Spaceflights.Scheduler/ ← executable (scheduler + dashboard)
```

The simplest deployment - one process that schedules and executes everything. The executable adds `AddScheduler()` and `AddTraxDashboard()`. Local workers are the implicit default when `UsePostgres()` is configured.

Good for: data pipelines, ETL jobs, background processing where a single server handles the load.

### Model 2: Separate API + Scheduler

**Sample:** `LocalWorkers/`

```
Trax.Samples.GameServer/            ← library (trains)
Trax.Samples.GameServer.Scheduler/  ← executable (scheduler + dashboard)
Trax.Samples.GameServer.Api/        ← executable (API only)
```

Two processes share the same trains library and Postgres database. The scheduler process handles background execution and hosts the dashboard. The GraphQL process serves the API - it can run lightweight trains synchronously and queue heavy work for the scheduler.

Because the two processes are separate, both call `UseBroadcaster(b => b.UseRabbitMq(...))`. The scheduler publishes train lifecycle events and coalesced data-change signals; the API subscribes and relays them to its GraphQL subscriptions. So `onTrainStateChanged` reflects trains the scheduler ran, and `onDataChanged` fires when the scheduler queues, dispatches, or dead-letters work, letting a dashboard update live without polling.

The split:
- **Scheduler:** `AddScheduler()` + `AddTraxDashboard()` + `UseBroadcaster()`
- **API:** `AddTraxGraphQL()` + `UseBroadcaster()` - no scheduler, no executor

Good for: web applications with both an API and background jobs, where the API needs to stay responsive and offload heavy work.

### Model 3: Hub + Distributed Workers

**Sample:** `DistributedWorkers/`

```
Trax.Samples.EnergyHub/         ← library (trains)
Trax.Samples.EnergyHub.Hub/     ← executable (API + scheduler + dashboard, no execution)
Trax.Samples.EnergyHub.Worker/  ← executable (worker only, no API)
```

The hub process manages scheduling and serves the API. To keep it from executing trains, register `PostgresJobSubmitter` through `OverrideSubmitter(s => s.AddScoped<IJobSubmitter, PostgresJobSubmitter>())`: jobs are written to the `background_job` table and no local workers start. Without the override, a scheduler on Postgres starts local workers and the hub runs trains too. Separate worker processes poll that table and execute trains.

The split:
- **Hub:** `AddScheduler(s => s.OverrideSubmitter(...))` + `AddTraxGraphQL()` + `AddTraxDashboard()`
- **Worker:** `AddTraxWorker()` - polls `background_job` with `FOR UPDATE SKIP LOCKED`

Workers scale horizontally - run as many as you need. The hub stays lightweight.

Good for: high-throughput systems, microservices, environments where you need to scale execution independently from scheduling.

### Model 4: Ephemeral Workers (Serverless)

**Sample:** `EphemeralWorkers/`

```
Trax.Samples.ContentShield/            ← library (trains)
Trax.Samples.ContentShield.Api/        ← executable (API + dashboard, HTTP dispatch)
Trax.Samples.ContentShield.Runner/     ← executable (ephemeral runner, no scheduler)
```

No scheduled jobs - all work is triggered by GraphQL mutations. The API dispatches queued mutations directly to the Runner via HTTP using `UseRemoteWorkers()`, and also offloads synchronous `run` mutations to the Runner via `UseRemoteRun()`. The Runner simulates a serverless function (AWS Lambda, Cloud Run, Azure Functions) - it receives requests over HTTP, executes the train, and returns.

The split:
- **API:** `AddScheduler()` + `UseRemoteWorkers()` + `UseRemoteRun()` + `AddTraxGraphQL()` + `AddTraxDashboard()`
- **Runner:** a `TraxLambdaFunction` run locally with `RunLocalAsync()`, which maps `/trax/execute` and `/trax/run`, plus `UseBroadcaster()` - no scheduler, no polling, no dashboard. The Runner needs an [authorization posture](/docs/scheduler/remote-execution#authorization-posture): a `SigningKey` in `ConfigureRunner` matching the API's `SigningKey` on `UseRemoteWorkers()` and `UseRemoteRun()`

Query trains (e.g. `LookupModerationResult`) run synchronously on the API process. Queued trains (e.g. `ReviewContent`, `SendViolationNotice`) are POSTed to the Runner by the HTTP job submitter that `UseRemoteWorkers()` registers. No `background_job` table is involved - jobs go directly over HTTP.

The Runner uses `UseBroadcaster(b => b.UseRabbitMq(...))` to publish lifecycle events back to RabbitMQ, so the API's GraphQL subscriptions are notified when queued trains complete.

Good for: serverless/FaaS deployments, on-demand workloads with zero idle cost, event-driven architectures where all work is API-triggered.

### Model 5: Single-Server with Real-Time Subscriptions

**Sample:** `ChatService/`

```
Trax.Samples.ChatService.Data/     ← data layer (EF Core entities, DbContext, migrations)
Trax.Samples.ChatService/          ← library (trains, lifecycle hook, subscription types)
Trax.Samples.ChatService.Api/      ← executable (single server)
Trax.Samples.ChatService.Client/   ← React + TypeScript frontend (Apollo Client, graphql-ws)
```

A single-server chat application that demonstrates how Trax lifecycle hooks can power domain-specific real-time GraphQL subscriptions. No scheduler or workers - everything runs in one process.

The key innovation is the `ChatLifecycleHook`, a custom `ITrainLifecycleHook` that intercepts completed chat mutation trains. When a `SendMessage` train completes, the hook reads `metadata.Output` (the serialized train output), extracts the `chatRoomId`, and publishes a `ChatSubscriptionEvent` to a room-scoped HotChocolate topic. Clients subscribed to that room receive the event in real time.

This approach works because:
- Chat mutation trains are decorated with `[TraxBroadcast]`, which causes lifecycle hooks to fire
- The hook is registered via `AddLifecycleHook<ChatLifecycleHook>()` on the effect builder
- A custom `ChatSubscriptions` type extends the "trax" GraphQL schema with `onChatEvent(chatRoomId: "...")` alongside the standard Trax lifecycle subscriptions

The sample also includes its own EF Core data layer in a separate project (`ChatService.Data`) with `ChatRoom`, `ChatParticipant`, and `ChatMessage` entities. The `ChatDbContext` uses the `chat` schema to coexist with Trax's `trax` schema in the same database.

A React + TypeScript frontend (`ChatService.Client`) demonstrates the full client/server GraphQL interaction. It uses Apollo Client with a split link - HTTP for queries/mutations and `graphql-ws` for subscriptions - connecting to the HotChocolate endpoint at `localhost:5210/trax/graphql`. The UI lets you switch between users (Alice, Bob, Charlie), create and join rooms, send messages, and see real-time subscription delivery in action.

Good for: real-time applications, chat systems, collaboration tools, notification feeds - anywhere you need domain-specific subscriptions driven by train completion events.

### Model 6: Hub with Built-In Subscriptions

**Sample:** `TestRunner/`

```
Trax.Samples.TestRunner/              ← library (trains, NUnit.Engine integration)
Trax.Samples.TestRunner.Hub/         ← executable (API + scheduler + local workers)
Trax.Samples.TestRunner.Client/      ← React + TypeScript frontend (Apollo Client, graphql-ws)
```

A single-process hub that runs NUnit tests across the Trax monorepo on demand. This sample demonstrates the simplest way to get real-time feedback from queued trains - using `[TraxBroadcast]` with the built-in `onTrainCompleted` subscription, with no custom lifecycle hook needed.

The `RunTestsTrain` is decorated with `[TraxMutation(GraphQLOperation.Queue)]` and `[TraxBroadcast]`. When a user clicks "Run" in the React frontend, a queue mutation returns an `externalId` immediately. Local workers pick up the job and execute two junctions:

1. **`BuildProjectJunction`** - runs `dotnet build` via `Process.Start` to compile the test project
2. **`ExecuteTestsJunction`** - uses NUnit.Engine (`TestEngineActivator.CreateInstance()`) to load the built DLL and run tests **in-process**, returning structured XML results parsed into a `TestResult` model

When the train completes, `[TraxBroadcast]` triggers the built-in `GraphQLSubscriptionHook`, which publishes a `TrainLifecycleEvent` to the `onTrainCompleted` subscription topic. The React frontend subscribes to this topic, filters events by train name (the interface FullName), and displays pass/fail counts, durations, and error details.

This differs from ChatService (Model 5) in a key way: ChatService uses a **custom** `ITrainLifecycleHook` to publish domain-specific events to custom subscription topics. TestRunner uses **no custom hook at all** - the standard `[TraxBroadcast]` attribute and built-in `onTrainCompleted` subscription handle everything. The frontend parses the train's serialized `output` from the subscription event to extract test results.

A `TestProjectRegistry` singleton service scans the monorepo for `.csproj` files containing NUnit package references, exposed through a `DiscoverTestProjectsTrain` query that populates the UI.

The Hub uses `ConfigureLocalWorkers(w => w.WorkerCount = 4)` for parallel test execution and `DefaultJobTimeout(TimeSpan.FromMinutes(30))` to accommodate longer-running test suites.

Good for: developer tools, CI dashboards, any scenario where queued train results need to reach the frontend without writing custom subscription infrastructure.

## Comparing the Models

| Capability | Standalone | Separate API | Distributed | Ephemeral | Chat (Real-Time) | TestRunner (Hub) |
|-----------|-----------|-------------|------------|-----------|-----------------|-----------------|
| Processes | 1 | 2 | 2+ | 2 | 1 | 1 |
| Scheduler | In-process | In-process (scheduler) | Hub (scheduling only) | API (dispatch only) | None | In-process |
| Execution | In-process | In-process (scheduler) | Workers (polling) | Runner (HTTP push) | In-process | In-process (local workers) |
| API | None | Separate process | Hub | In-process | In-process | In-process |
| Dashboard | In-process | In scheduler | In hub | In API | None | None |
| Job table | `background_job` | `background_job` | `background_job` | None (direct HTTP) | None | `background_job` |
| Horizontal scaling | No | No | Workers scale independently | Runner auto-scales | No | No |
| Subscriptions | No | No | No | No | Custom lifecycle hook | Built-in `[TraxBroadcast]` |

In all models, the trains library is identical. Only the `Program.cs` files differ.

## Running the Samples

All samples require PostgreSQL. From the `Trax.Samples/` directory:

```bash
docker compose up -d
```

The compose file publishes Postgres and RabbitMQ on `127.0.0.1` only, because their passwords
(`trax123`) are written in the file. Set `TRAX_PG_PORT` to move Postgres to another host port
when something else holds 5432. If you copy the file to a server, keep the `127.0.0.1:` prefix
and replace the passwords.

The samples' demo API keys and JWT signing keys are published in this repository, so each
sample registers them only in Development. `dotnet run` starts in Development through the
project's `Properties/launchSettings.json`; started any other way, a sample accepts none of
them. Every demo API key contains `do-not-use-in-production`, which Trax.Api refuses to start
with outside Development.

### DataPipeline (Standalone)

```bash
dotnet run --project samples/DataPipeline/Trax.Samples.Flowthru.Spaceflights.Scheduler
```

Dashboard at `http://localhost:5000/trax`.

### LocalWorkers (Separate API + Scheduler)

```bash
# Terminal 1 - scheduler
dotnet run --project samples/LocalWorkers/Trax.Samples.GameServer.Scheduler

# Terminal 2 - API
dotnet run --project samples/LocalWorkers/Trax.Samples.GameServer.Api
```

Dashboard at `http://localhost:5201/trax`. GraphQL IDE at `http://localhost:5200/trax/graphql`.

#### E2E Tests

The GameServer sample includes a full E2E test suite that validates scheduler dispatch, dependency chains, dormant dependent activation, dead-letter flows, and GraphQL authorization against a real Postgres database:

```bash
cd Trax.Samples && docker compose up -d
dotnet test --filter "FullyQualifiedName~GameServer.E2E"
```

See [E2E Testing](/docs/cross-cutting/e2e-testing) for the patterns used.

### DistributedWorkers (Hub + Workers)

```bash
# Terminal 1 - hub
dotnet run --project samples/DistributedWorkers/Trax.Samples.EnergyHub.Hub

# Terminal 2 - worker
dotnet run --project samples/DistributedWorkers/Trax.Samples.EnergyHub.Worker
```

Dashboard at `http://localhost:5202/trax`. GraphQL IDE at `http://localhost:5202/trax/graphql`.

#### E2E Tests

```bash
cd Trax.Samples && docker compose up -d
dotnet test --filter "FullyQualifiedName~EnergyHub.E2E"
```

Tests cover manifest configuration (dependency chains, batch scheduling, cron), train completion via `TrainBus.RunAsync`, GraphQL queries/mutations, and the solar-to-battery dependency chain through the scheduler.

### EphemeralWorkers (API + Serverless Runner)

```bash
# Terminal 1 - runner
dotnet run --project samples/EphemeralWorkers/Trax.Samples.ContentShield.Runner

# Terminal 2 - API
dotnet run --project samples/EphemeralWorkers/Trax.Samples.ContentShield.Api
```

Dashboard at `http://localhost:5204/trax`. GraphQL IDE at `http://localhost:5204/trax/graphql`.

### ChatService (Single-Server Real-Time)

```bash
# Terminal 1 - API
dotnet run --project samples/ChatService/Trax.Samples.ChatService.Api

# Terminal 2 - React client (optional)
cd samples/ChatService/Trax.Samples.ChatService.Client
npm install && npm run dev
```

GraphQL IDE at `http://localhost:5210/trax/graphql`. React client at `http://localhost:5173`.

Authentication uses `X-Api-Key` header with three users: `alice-key-do-not-use-in-production`, `bob-key-do-not-use-in-production`, `charlie-key-do-not-use-in-production`.
The React client provides a user switcher dropdown - open multiple browser tabs to simulate different users chatting in real time.

**Quick walkthrough (ChatService):**

```graphql
# 1. Create a room (as Alice)
mutation { dispatch { createChatRoom(input: { name: "General", userId: "alice", displayName: "Alice" }) { externalId output { chatRoomId name } } } }

# 2. Join the room (as Bob) - use the chatRoomId from step 1
mutation { dispatch { joinChatRoom(input: { chatRoomId: "<id>", userId: "bob", displayName: "Bob" }) { externalId output { joinedAt } } } }

# 3. Subscribe to real-time events (in a second tab)
subscription { onChatEvent(chatRoomId: "<id>") { eventType payload timestamp } }

# 4. Send a message - the subscription tab receives it
mutation { dispatch { sendMessage(input: { chatRoomId: "<id>", senderUserId: "alice", content: "Hello Bob!" }) { externalId output { messageId content sentAt } } } }

# 5. Query chat history
{ discover { getChatHistory(input: { chatRoomId: "<id>" }) { messages { senderDisplayName content sentAt } } } }
```

#### E2E Tests

```bash
cd Trax.Samples && docker compose up -d
dotnet test --filter "FullyQualifiedName~ChatService.E2E"
```

Tests cover chat room CRUD, message persistence, participant management, Trax metadata verification, and lifecycle subscriptions via WebSocket.

### TestRunner (Hub with Built-In Subscriptions)

```bash
# Terminal 1 - Hub (API + scheduler + local workers)
dotnet run --project samples/TestRunner/Trax.Samples.TestRunner.Hub

# Terminal 2 - React client
cd samples/TestRunner/Trax.Samples.TestRunner.Client
npm install && npm run dev
```

GraphQL IDE at `http://localhost:5220/trax/graphql`. React client at `http://localhost:5173`.

No authentication - this is a local developer tool, and it builds and runs code on your
machine. The hub starts only in Development (which `dotnet run` sets through its
`launchSettings.json`), listens on localhost, and answers only requests addressed to
`localhost`. `runTests` takes a project name from `discoverTestProjects` and refuses any other,
so it runs only the test projects already under the configured root.

**Quick walkthrough (TestRunner):**

```graphql
# 1. Discover all test projects in the monorepo
{ discover { discoverTestProjects(input: {}) { projects { name repoName projectPath requiresPostgres } } } }

# 2. Subscribe to train completion events (in a second tab)
subscription { onTrainCompleted { externalId trainName output } }

# 3. Queue a test run - returns immediately with an externalId
mutation { dispatch { runTests(input: { projectName: "Trax.Core.Tests.Unit" }) { externalId workQueueId } } }

# The subscription tab receives the result when the train completes,
# with test results in the output field (Total, Passed, Failed, FailedTests, etc.)
```

### Bookworm (Multi-Schema with Cross-Schema GraphQL)

```bash
# Start Postgres, then run the API
cd Trax.Samples && docker compose up -d
dotnet run --project samples/Bookworm/Trax.Samples.Bookworm.Api
```

```bash
# Resolve every loan's catalog book across schemas in one batched query
curl -H "X-Api-Key: member-key-do-not-use-in-production" \
     -X POST http://localhost:5210/trax/graphql -H "Content-Type: application/json" \
     -d '{"query":"{ discover { lending { loans { nodes { id bookId book { title isbn } } } } } }"}'
```

The `loans` query lives in the `lending` schema; the nested `book` is resolved from the `catalog` schema by the batched cross-schema loader.

## SDK Reference

> [AddTrax / AddEffects](/docs/sdk-reference/configuration) | [UsePostgres](/docs/sdk-reference/configuration/add-postgres-effect) | [AddJson](/docs/sdk-reference/configuration/add-json-effect) | [SaveTrainParameters](/docs/sdk-reference/configuration/save-train-parameters) | [AddJunctionProgress](/docs/sdk-reference/configuration/add-junction-progress) | [AddLifecycleHook](/docs/sdk-reference/configuration/add-lifecycle-hook) | [UseBroadcaster](/docs/sdk-reference/configuration/use-broadcaster) | [AddMediator](/docs/sdk-reference/configuration/add-mediator) | [AddScheduler](/docs/sdk-reference/scheduler-api/add-scheduler) | [ConfigureLocalWorkers](/docs/sdk-reference/scheduler-api/use-local-workers) | [UseRemoteWorkers](/docs/sdk-reference/scheduler-api/use-remote-workers) | [UseRemoteRun](/docs/sdk-reference/scheduler-api/use-remote-run) | [AddTraxWorker](/docs/sdk-reference/scheduler-api/add-trax-worker) | [AddTraxJobRunner](/docs/sdk-reference/scheduler-api/add-trax-job-runner) | [AddTraxDashboard](/docs/sdk-reference/dashboard-api/add-trax-dashboard) | [UseTraxDashboard](/docs/sdk-reference/dashboard-api/use-trax-dashboard) | [AddTraxGraphQL](/docs/sdk-reference/graphql-api/add-trax-graphql) | [UseTraxGraphQL](/docs/sdk-reference/graphql-api/add-trax-graphql) | [TraxQuery / TraxMutation](/docs/sdk-reference/graphql-api/trax-graphql-attribute) | [TraxBroadcast](/docs/sdk-reference/graphql-api/trax-broadcast-attribute) | [TraxQueryModel](/docs/sdk-reference/graphql-api/query-models)
