# Scaffold the workspace

You are building the team layer of an operations workspace — the part that will decide who is on the team, where team records live, and which routes a caller may reach. Every chapter in this track adds one layer to that single app. This first chapter lays the foundation: a real NetScript workspace on disk named `my-workspace/`, with an example service and a Postgres database, booted under Aspire. You will not write auth yet — you will create the base that the auth backend, the team database, the provisioning job, and the route guards all attach to in the chapters that follow. Everything that later fails closed or provisions off-path attaches to what you scaffold here, so the checkpoint at the end of this chapter — a healthy service and a clean type-check — is the ground truth the rest of the track builds on.

1. [1 · Scaffold](https://rickylabs.github.io/netscript/netscript/tutorials/workspace/01-scaffold/)
2. [2 · Auth](https://rickylabs.github.io/netscript/netscript/tutorials/workspace/02-auth/)
3. [3 · Workspace data](https://rickylabs.github.io/netscript/netscript/tutorials/workspace/03-workspace-data/)
4. [4 · Provision job](https://rickylabs.github.io/netscript/netscript/tutorials/workspace/04-provision-job/)
5. [5 · Route authz](https://rickylabs.github.io/netscript/netscript/tutorials/workspace/05-route-authz/)
6. [6 · Deploy](https://rickylabs.github.io/netscript/netscript/tutorials/workspace/06-deploy/)

## What you will build

A working `my-workspace/` directory: a typed `workspace` service on its own port, a Postgres database, the Aspire orchestration that provisions Postgres and the Redis cache, and a clean `deno task check`. By the end you will have the Aspire dashboard open on `https://localhost:18888` showing your service, database, and cache running together — the control plane you keep up for the rest of the track.

## Before you begin

This is the first chapter, so there is no prior project state to check — but your local toolchain must be ready. You need **[Deno](https://deno.com/) 2.x**, the **[Aspire CLI](https://aspire.dev)**, and **Docker** running so Aspire can start the Postgres and Redis containers. Verify all three:

```sh
deno --version     # Deno 2.x
aspire --version   # the .NET Aspire CLI
docker info        # prints engine details, not a connection error
```

Install the NetScript CLI from JSR once, then confirm it is on your `PATH`:

```sh
deno install --global --allow-all --name netscript jsr:@netscript/cli@0.0.6
netscript --help
```

`netscript --help` should list the command groups `agent`, `config`, `deploy`, `init`, `contract`, `db`, `generate`, `marketplace`, `plugin`, `service`, `ui:add`, `ui:init`, `ui:list`, `ui:update`, and `ui:remove`. If `netscript` is not found, make sure Deno's install directory (printed by `deno install`) is on your `PATH`, then open a fresh terminal.

> Prefer not to install globally?
>
> Run any command ad-hoc with
>
> deno x jsr:@netscript/cli@0.0.6 <command>
>
> . The rest of this track assumes the installed
>
> netscript
>
> form.

## Step 1 — Preview the scaffold

Before writing files, ask the CLI what it *would* create. `--dry-run` plans the scaffold and prints per-phase file and directory totals without touching disk:

```sh
netscript init my-workspace --service --service-name workspace --service-port 3001 --db postgres --dry-run
```

A clean dry run validates your flag combination — it rejects bad mixes (an unknown `--db` engine, for example) here, before any files exist. Treat a clean plan as a green light to scaffold for real.

This track uses `--db postgres`, the recommended default. The database is polyglot, so you can swap `--db postgres` for `mysql`, `mssql`, or `sqlite` and follow along the same way (sqlite is file-backed, so it has no Aspire container).

## Step 2 — Create `my-workspace/`

Run the same command without `--dry-run`. This track uses an example service named `workspace` on port `3001` (note: this tutorial pins the port to 3001; in unpinned scaffolds, each project is allocated its own randomized high-range ports) and a Postgres database, so you have something real to authenticate and protect later:

```sh
netscript init my-workspace --service --service-name workspace --service-port 3001 --db postgres
cd my-workspace
```

This scaffolds `my-workspace/`, formats the output with `deno fmt`, and initializes a git repository. On completion the CLI prints a **next steps** summary tailored to your options.

**netscript init options used here**

| Name | Type | Description |
| --- | --- | --- |
| `--service --service-name workspace --service-port 3001` | `example service` | Include an example oRPC service named 'workspace' on port 3001 — the service you protect in chapter 5. |
| `--db postgres` | `database workspace` | Scaffold a Postgres database workspace. The auth plugin's auth.prisma migration (chapter 2) aggregates into this primary datasource. |
| `--dry-run` | `plan only` | Plan the scaffold and print totals without writing any files (Step 1). |

## Step 3 — Tour what you got

Open `my-workspace/` and you will find this shape. These are the directories the rest of the track edits:

- my-workspace/

  - apps/dashboard/ # Fresh frontend (defineFreshApp)
  - contracts/ # Shared oRPC + Zod contracts, versioned under versions/v1/
  - services/workspace/ # The example oRPC service (src/main.ts, router.ts) — guarded in chapter 5
  - plugins/ # Plugin registry — empty until you add the auth plugin in chapter 2
  - database/ # The primary Postgres workspace; a second db is added in chapter 3
  - aspire/ # Aspire TypeScript orchestration (isolated Node runtime)
  - appsettings.json # Infrastructure config (Services / Databases / Persistent)
  - deno.json # Workspace root (members, tasks, dependency catalog)
  - netscript.config.ts # Framework config (defineConfig)

The two pieces you will return to most: **`services/workspace/`** is the oRPC service whose routes you protect in chapter 5, and **`plugins/`** is where the `auth` plugin lands in chapter 2. Everything derives its types from `contracts/` — the typed seam between your services and any client.

> Where is packages/?
>
> If you scaffolded from a checkout of the NetScript repo you may see a vendored
>
> packages/
>
> directory. A normal JSR install does not have one — your project pulls
>
> @netscript/*
>
> from the registry. Ignore
>
> packages/
>
> throughout this track.

## Step 4 — Bring up orchestration

This is the step that turns a folder of files into a running system. **Aspire provisions your database and cache; you do not start containers by hand, and you run it before any `netscript db` command.** Run it from the `aspire/` subfolder so the CLI finds `apphost.mts`:

```sh
cd aspire
aspire restore   # once per machine: restores the Aspire SDK modules
aspire start       # starts the AppHost and every declared resource
```

`aspire start` brings up the Postgres database, the Redis cache, and your `workspace` service together, then prints the **Aspire dashboard** URL and a one-time login token. Open it:

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

Leave `aspire start` running in this terminal — it is your app's control plane for the entire track. The dashboard's resource list is the authority for the exact port each resource bound.

> Aspire is step 2 — database commands need it running
>
> The Postgres container only exists while
>
> aspire start
>
> is up. So
>
> netscript db init
>
> ,
>
> db generate
>
> , and
>
> db seed
>
> — which you use from chapter 2 onward — must run
>
> after
>
> Aspire has started, never before. A db command with no Aspire up fails fast: there is no Postgres for it to reach.

## Verify your progress

In a second terminal (leave `aspire start` going in the first), confirm the `workspace` service is up and the workspace type-checks:

```sh
curl http://localhost:3001/health
deno task check
```

`curl` should return a healthy JSON response, and `deno task check` should finish clean — confirming the scaffold, contracts, and service all line up.

- [ ] `netscript --help` lists the public command groups.
- [ ] `my-workspace/` exists with `services/workspace/`, `plugins/`, `database/`, and `aspire/`.
- [ ] `aspire start` is up; `postgres` and `redis` are green in the dashboard at `https://localhost:18888`.
- [ ] `curl http://localhost:3001/health` returns a healthy response.
- [ ] `deno task check` passes.

> If something is not green
>
> Three checks cover most first-run snags: (1) is
>
> aspire start
>
> still up, with
>
> postgres
>
> and
>
> redis
>
> healthy in the dashboard? (2) is Docker running (
>
> docker info
>
> )? (3) did you
>
> cd aspire
>
> before
>
> aspire start
>
> , so it found
>
> apphost.mts
>
> ? A failed
>
> curl
>
> usually means the service has not finished starting — wait a few seconds and retry.

## What you built

A real NetScript workspace at `my-workspace/`: a typed `workspace` service on `:3001`, shared contracts, a Postgres database, and a Redis cache — all orchestrated by Aspire and visible in one dashboard. This is the base — and right now it answers absolutely anyone. Next you fix that: you add an identity layer, so the app can tell a team member from a stranger.

[Team Workspace](https://rickylabs.github.io/netscript/netscript/tutorials/workspace/) [2 · Auth](https://rickylabs.github.io/netscript/netscript/tutorials/workspace/02-auth/)
