# Run the whole dashboard under Aspire

You have built the full spine — contract, service, query layer, page, and live stream. This final chapter steps back and runs the entire graph as one system: the `orders` service, the Fresh dashboard, the durable-streams runtime, Postgres, and Redis, all under a single `aspire start`. That is the payoff of scaffolding on NetScript — you never wrote a compose file, a container manifest, or a service-discovery config, yet one command boots the whole graph in dependency order, resolves every cross-reference into injected environment variables, and hands you a live view of it. This chapter is also precise about what local Aspire is — a development orchestrator — and what it is not.

1. [1 · Scaffold](https://rickylabs.github.io/netscript/netscript/tutorials/live-dashboard/01-scaffold/)
2. [2 · Contract to service](https://rickylabs.github.io/netscript/netscript/tutorials/live-dashboard/02-contract-to-service/)
3. [3 · Cache-first query](https://rickylabs.github.io/netscript/netscript/tutorials/live-dashboard/03-sdk-cache-first-query/)
4. [4 · definePage + island](https://rickylabs.github.io/netscript/netscript/tutorials/live-dashboard/04-definePage-QueryIsland/)
5. [5 · Live stream](https://rickylabs.github.io/netscript/netscript/tutorials/live-dashboard/05-live-stream/)
6. [6 · Deploy](https://rickylabs.github.io/netscript/netscript/tutorials/live-dashboard/06-deploy/)

## What you will build

A complete, observable run of `my-dashboard/` on one machine: every resource your dashboard touches, booted in dependency order from a single command, with the wiring resolved into injected environment variables and the whole graph visible in the Aspire dashboard at `:18888`. You finish the track with a running live dashboard you can open, watch, and trust you understand.

## Before you begin

You should have completed [chapter 5](https://rickylabs.github.io/netscript/tutorials/live-dashboard/05-live-stream/): the live monitor receiving mirrored saga state over its open subscription. The AppHost was scaffolded back in chapter 1 (you did not pass `--no-aspire`), so confirm the orchestration entry points are on disk — from the workspace root:

```sh
ls aspire/apphost.mts aspire/aspire.config.json
```

Both files should exist. `apphost.mts` is the **TypeScript/Node** program `aspire start` executes; `aspire.config.json` pins the SDK. `netscript init` generated them — you never hand-write the AppHost.

> The graph is derived from your plugins
>
> The resource graph inside the AppHost is assembled at boot from your installed plugins via
>
> composeAppHost
>
> — add the sagas plugin (chapter 5) and its API plus its streams runtime appear; remove it and they vanish, no edit to
>
> apphost.mts
>
> . The mechanics are in
>
> Orchestration with Aspire
>
> .

## Step 1 — Restore the AppHost SDK (once)

The AppHost runs on its own isolated Node runtime inside `aspire/` so its dependencies never leak into your Deno workspace. Restore that runtime once per machine (and after an SDK bump):

```sh
cd aspire
aspire restore
```

## Step 2 — Start the whole graph

A single `aspire start` translates `appsettings.json` plus the plugin contributions into a resource graph and boots all of it — infrastructure first, then the service, the Fresh app, and the streams runtime, with cross-references resolved into injected environment variables:

```sh
# Still inside aspire/. Prints the dashboard URL + a one-time login token.
aspire start
```

When boot finishes, `aspire start` prints the dashboard address and a login token. The graph it stands up for this track:

**What aspire start brings up for my-dashboard**

| Name | Type | Description |
| --- | --- | --- |
| `aspire (dashboard)` | `https://localhost:18888` | The Aspire dashboard: live resource list, console logs, structured logs and traces. A login token prints on start. |
| `postgres` | `Container` | Provisioned via Docker. The database your orders service reads — reachable only once Aspire is up. |
| `redis` | `Container (cache)` | Redis cache — the default `--cache-backend`; Redis-compatible. Backs the KV-backed query layer from chapter 3. |
| `orders` | `:3002` | Your oRPC service (defineService). RPC at /api/rpc/*, OpenAPI at /api/v1/orders/*. |
| `dashboard (Fresh app)` | `allocated port` | Your Fresh frontend — the live dashboard you built in chapters 4–5. Read its port from the dashboard resource list. |
| `streams` | `allocated port` | Durable-streams producer runtime, present once the sagas plugin is installed. Feeds useLiveQuery. |

> Only pinned ports are predictable — read the dashboard for the rest
>
> orders
>
> answers on
>
> :3002
>
> because you pinned it with
>
> --service-port
>
> in chapter 1. Nothing else in this graph has a memorizable number, for two different reasons. The Fresh
>
> dashboard
>
> app pins no host port, so
>
> Aspire allocates one at runtime
>
> — a fresh number each start. A plugin runtime installed without
>
> --port
>
> does get a pinned host port, but the installer picks it: a hash of your project name over the IANA dynamic range (
>
> 49152–65535
>
> ), probing past ports already claimed in this workspace. That spreads projects apart in practice; it is not a guarantee, since the range is finite and workspaces cannot see each other's allocations. The Aspire dashboard's resource list is the authority for both — read every unpinned port from there.

## Step 3 — Open the live dashboard

With the graph up, find the `dashboard` resource in the Aspire resource list, open its endpoint, and append the orders route you built:

```
/dashboard/orders/
```

The table renders cache-first (chapter 4), refetches on the client, and — if the streams runtime is up — the saga monitor receives pushed updates over its subscription (chapter 5). Then open the Aspire dashboard to watch the system behind it:

```
https://localhost:18888
```

Paste the login token `aspire start` printed. You get one pane over the running graph:

**Aspire dashboard surfaces**

| Name | Type | Description |
| --- | --- | --- |
| `Resources` | `tab` | Every container and process with status, endpoints, and resolved environment. The authority for which port each resource bound. |
| `Console logs` | `tab` | stdout/stderr per resource — a failing service or streams runtime is one click away, not buried in a terminal. |
| `Structured logs + Traces` | `tab` | Spans your handlers and pages emit (including the withTelemetry span from chapter 4), correlated across resources via the OTLP collector on :4318. |

## Step 4 — Watch a request flow through the graph

Create an order and follow it across the whole stack in one place. From a second terminal:

```sh
curl -X POST http://localhost:3002/api/v1/orders/create \
  -H 'content-type: application/json' \
  -d '{ "userId": 1, "total": 49.9, "status": "pending", "shippingStreet": "1 Main", "shippingCity": "Berlin", "shippingCountry": "DE", "shippingZipCode": "10115", "items": [{ "productId": 1, "quantity": 1 }] }'
```

In the **Resources** tab you can see the `orders` service handle it; in **Traces** the request shows as a span; the live monitor in your Fresh app advances a new saga row; and the orders table picks up the new order on its next revalidation. One command, the full spine, observable end to end.

## Verify your progress

```sh
# From the workspace root, with `aspire start` up in another terminal:
curl http://localhost:3002/health      # orders service — pinned, so the port is known

# The streams runtime's port is allocated, not pinned. Copy its endpoint from the
# Aspire resource list, then:
curl <streams-endpoint>/health         # streams runtime (if sagas installed)
```

Both should return healthy responses, and the dashboard at `:18888` should list `postgres`, `redis`, `orders`, the Fresh `dashboard`, and `streams` all running.

- [ ] `aspire restore` then `aspire start` boots the graph without errors.
- [ ] `https://localhost:18888` lists every resource healthy.
- [ ] The `dashboard` app's endpoint (from the resource list) + `/dashboard/orders/` renders the live table.
- [ ] Creating an order is visible in the dashboard traces and the live monitor.

> Aspire is the LOCAL story — not a production deployer
>
> aspire start
>
> exists to make one command produce a complete, observable, correctly-wired stack on
>
> one machine
>
> . The Postgres and Redis it starts are throwaway Docker containers for dev convenience —
>
> not
>
> your production database or cache. For a remote target you point processes at managed infrastructure and let your platform own lifecycle; that is the
>
> Deploy
>
> recipe, and the
>
> --no-aspire
>
> path in
>
> Orchestration with Aspire
>
> .

> Footguns when aspire start will not boot
>
> - **Docker not running.** Aspire provisions Postgres + Redis through Docker; no daemon means the happy path does not start. Start Docker, or take the `--no-aspire` path with your own infrastructure.
> - **Wrong directory.** `aspire restore` and `aspire start` run from inside `aspire/`; `netscript db` commands run from the workspace root. Mixing them up is the most common first-run error.
> - **db command before `aspire start`.** Every `netscript db` command needs a live Postgres — bring the graph up first.
> - **Ports in use.** The dashboard wants `:18888`/`:18889` and OTLP `:4318`; the pinned `orders` service claims `:3002`, the streams runtime claims an installer-allocated port in `49152–65535`, and the Fresh app claims whatever Aspire hands it at start. A stale prior run holding a port blocks boot — free it.

## What you built

You ran the complete `my-dashboard/` graph under one `aspire start`: service, Fresh app, durable streams, Postgres, and Redis, wired automatically and observable in the dashboard at `:18888`. That closes the track — the order queue from the opening premise is real now: no polling loop, no refresh button, no window where a cancelled order looks shippable. You built and ran it on the full NetScript spine.

## Where to go next

- **Task recipes** → the [how-to guides](https://rickylabs.github.io/netscript/how-to/) cover what the tutorials don't: adding plugins, database migrations, queue backends, and production pitfalls.
- **Ship it remotely** → [Deploy](https://rickylabs.github.io/netscript/orchestration-runtime/how-to/deploy/) is the production companion to local Aspire.
- **Go deeper** → [Orchestration with Aspire](https://rickylabs.github.io/netscript/explanation/aspire/) explains the AppHost, plugin contributions, and two-pass reference resolution; [Durable streams](https://rickylabs.github.io/netscript/netscript/durable-workflows/streams/) and

[the Fresh meta-framework](https://rickylabs.github.io/netscript/netscript/web-layer/) back chapters 4 and 5.

[5 · Live stream](https://rickylabs.github.io/netscript/netscript/tutorials/live-dashboard/05-live-stream/) [How-to guides](https://rickylabs.github.io/netscript/netscript/how-to/)
