# Scaffold the workspace

This is the first chapter of the ERP Sync track — the service that keeps **Microsoft Dynamics**, the ERP your team is migrating to, fed from the file exports of **SAP**, the legacy system that is still the system of record. Before you can watch the SAP file drops or run background jobs, you need a workspace with the right plugins installed and an orchestrator to run them. In this chapter you create `my-erp/`, add the **workers** and **triggers** plugins, and boot the whole stack under Aspire so the rest of the track has something real to build on.

1. [1 · Scaffold](https://rickylabs.github.io/netscript/netscript/tutorials/erp-sync/01-scaffold/)
2. [2 · Import job](https://rickylabs.github.io/netscript/netscript/tutorials/erp-sync/02-import-job/)
3. [3 · Polyglot transform](https://rickylabs.github.io/netscript/netscript/tutorials/erp-sync/03-polyglot-transform/)
4. [4 · Queue & cron](https://rickylabs.github.io/netscript/netscript/tutorials/erp-sync/04-queue-and-cron/)
5. [5 · Deploy](https://rickylabs.github.io/netscript/netscript/tutorials/erp-sync/05-deploy/)

## What you will build

By the end of this chapter you will have `my-erp/` on disk — a NetScript workspace with a Postgres database, the **workers** plugin at `plugins/workers/` (the background-job engine), and the **triggers** plugin at `plugins/triggers/` (the ingress engine) — all running together under one `aspire start`, with the Aspire dashboard live on `:18888`.

## Before you begin

You need the same local toolchain the [main tutorials](https://rickylabs.github.io/netscript/tutorials/) use:

- **[Deno](https://deno.com/) 2.x** on your `PATH` — check with `deno --version`.
- The **[Aspire CLI](https://aspire.dev)** — check with `aspire --version`. Aspire provisions your database and cache locally so you do not wire up Docker by hand.
- **Docker** running, so Aspire can start the Postgres and Redis containers — confirm with `docker info`.

Install the NetScript CLI from JSR once, then confirm it:

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

You should see the public command groups, including `init`, `plugin`, `generate`, `db`, and `deploy`. If `netscript` is not found, make sure Deno's install directory is on your `PATH` and 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 with a dry run

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

```sh
netscript init my-erp --dry-run
```

A clean dry run means your flag combination is valid — the CLI rejects bad option mixes (an unknown `--db` engine, for instance) here, before any files exist. Treat it as a green light to scaffold for real.

## Step 2 — Create the workspace

This track needs a database to anchor durable execution, so scaffold with Postgres — the recommended default. `--db` is polyglot: swap `postgres` for `mysql`, `mssql`, or `sqlite` and the rest of the track works the same (this track uses `postgres` throughout):

```sh
netscript init my-erp --db postgres
cd my-erp
```

This scaffolds `my-erp/`, 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 (run netscript init --help for the full list)**

| Option | What it does |
| --- | --- |
| --db postgres | Scaffold a Postgres database workspace. Durable execution and queue persistence anchor on it. |
| --dry-run | Plan the scaffold and print totals without writing any files. |
| --no-aspire | Skip the Aspire orchestration files. Do NOT pass this — the rest of the track runs under Aspire. |

> 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/
>
> in this track.

## Step 3 — Add the workers plugin

NetScript's background capabilities arrive as plugins. Add the **workers** plugin with its sample jobs so you have a working reference to read and adapt:

```sh
netscript plugin install worker --name workers --samples
```

This lands the plugin at **`plugins/workers/`** — the canonical, config-referenced install location — and registers it in `netscript.config.ts` (`./plugins/workers/mod.ts`) and `appsettings.json`. The workers plugin ships an API service and a separate background processor that drains the job queue.

## Step 4 — Add the triggers plugin

Now add the **triggers** plugin, which is how NetScript receives events — including the file-watch trigger you build in [Chapter 2](https://rickylabs.github.io/netscript/tutorials/erp-sync/02-import-job/):

```sh
netscript plugin install trigger --name triggers --samples
```

This lands a workspace at `plugins/triggers/` and registers it in `netscript.config.ts` (`./plugins/triggers/mod.ts`) and `appsettings.json`. Confirm both plugins registered:

```sh
netscript plugin list
```

You should see `workers` and `triggers` in the registry.

> Two trees, one canonical home
>
> A scaffold may also create slimmer top-level
>
> workers/
>
> and
>
> triggers/
>
> directories — workspace members that stage a subset of files for the background processors. The real, config-referenced plugins live at
>
> `plugins/workers/`
>
> and
>
> `plugins/triggers/`
>
> : that is what
>
> netscript.config.ts
>
> points at and where you author code. Edit under
>
> plugins/
>
> .

## Step 5 — Bring up orchestration

This is the step that turns a folder of files into a running system. **Aspire provisions your database and cache and starts every process; 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 sees `apphost.mts`:

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

`aspire start` brings up Postgres, the Redis cache, the workers API + processor, and the triggers API

- processor together, then prints a URL and a one-time login token for the **Aspire dashboard**:

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

The dashboard's **Resources** tab is the authority for which port each resource bound, and there is no shortcut around it: because you installed these plugins without `--port`, the installer picked their host ports — a hash of your *project name* over the IANA dynamic range (`49152–65535`), probing past ports this workspace already claimed. Two of your own projects land apart in practice, though nothing guarantees it across machines or pins. Copy the `workers-api` and `triggers-api` endpoints from that tab now — the rest of the track writes them as `<workers-endpoint>` and `<triggers-endpoint>`. Leave `aspire start` going in this terminal; it is your control plane for the rest of the track.

> 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
>
> must run
>
> after
>
> Aspire has started — never before. There is more on the database sequence in
>
> Deploy locally with Aspire
>
> .

## Verify your progress

In a second terminal (leave `aspire start` going in the first), confirm both plugin APIs are alive:

```sh
curl <workers-endpoint>/health    # workers API
curl <triggers-endpoint>/health   # triggers API
```

Both should return a healthy JSON response. Then type-check the whole workspace from the project root:

```sh
deno task check
```

Expected: a clean check with no errors — the scaffold, both plugins, and the database wiring all line up.

- [ ] `my-erp/` exists with `plugins/workers/` and `plugins/triggers/` on disk.
- [ ] `netscript plugin list` shows both `workers` and `triggers`.
- [ ] `aspire start` is up; the dashboard on `:18888` lists `postgres`, `redis`, and both plugin APIs.
- [ ] `curl <workers-endpoint>/health` and `curl <triggers-endpoint>/health` both return healthy.
- [ ] `deno task check` is clean.

> 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 a service is still starting — give it a few seconds and retry.

## What you built

A real NetScript workspace, `my-erp/`, with the **workers** and **triggers** plugins installed and the whole stack — Postgres, Redis, both plugin APIs and their background processors — running under one `aspire start` and visible in the dashboard. Next, you give it the SAP export to ingest.

[ERP Sync](https://rickylabs.github.io/netscript/netscript/tutorials/erp-sync/) [2 · Import job](https://rickylabs.github.io/netscript/netscript/tutorials/erp-sync/02-import-job/)
