+ + {{ item.data.nav_title || item.data.title }} + + +
+` is
+ clickable and `order`-sort is hazard-free (no page-less intermediate nodes).
+
+**`base.vto` (replace lines 47–72).** Loop `navLanes`:
+
+- Render each lane divider:
+ `{{ lane.label }}
`
+ (subtitles are a kept SOTA affordance — Stripe/Lume pattern).
+- `kind:"flat"` → curated ``s; active check `page.data.url == href` (drop today's double
+ `|> url` dance — source==source, since base_path rewrites the emitted href).
+- `kind:"menu"` → for each root, feed `nav.menu(root, "nav_hide!=true", "order url").children`
+ to a recursive **`_includes/menu_item.vto`** partial (the plugin's documented recursion — required
+ because Build reaches depth 3 at pillar→how-to→recipe and Reference/Learn reach
+ unit/track→subpage). Each folder node is ``;
+ leaves are ``. Per-lane icons come
+ from `navLanes`; per-page icons are dropped (nav data carries none).
+
+**Collapse:** default-collapsed siblings, auto-expand the active path via `url.startsWith`. Native
+`/`, zero JS. A reader sees ~5 lane headers + 9 pillar summaries + one open
+subtree — strictly fewer on-screen elements than today's 120.
+
+**Breadcrumb (`breadcrumb.vto`).** Replace the `navSections` scan with `nav.breadcrumb(url)`
+(root→page array), prefixed by the owning lane label via a small `root→lane` lookup (so
+`/web-layer/query/` reads *Build › Web Layer › Data loading & cache*). Guard `{{ if crumb.data }}`.
+Behavior change accepted: one deterministic trail replaces "last matching section."
+
+**Next/prev (`nextPrev.vto`).** Keep front-matter-driven for tutorials (per-chapter editorial
+control; it never touched `navSections`, cannot break). For Reference (32 units, unmaintainable by
+hand) auto-derive with the verified v2.5.4 signature — `nav.nextPage(url, query?, sort?)` /
+`nav.previousPage(url, query?, sort?)` (there is **no** basePath argument; nav.ts:85/111). Scope to
+the reference lane via the query: `nav.nextPage(url, "url^=/reference/", "order url")`.
+
+**Untouched chrome (verified nav-agnostic):** `
below.
- **Learn** — the [Team Workspace tutorial, step 03](/tutorials/workspace/03-workspace-data/)
wires workspace data through the database from scratch.
-- **Do** — the [Use a second database](/how-to/use-a-second-database/) recipe adds a second
+- **Do** — the [Use a second database](/data-persistence/how-to/use-a-second-database/) recipe adds a second
adapter-backed datasource (MSSQL or MySQL) beside the primary Postgres.
## How persistence is wired
@@ -315,7 +316,7 @@ A second engine is a separate Prisma schema workspace (for example
database/mysql/schema/) with its own generate output and its own
migrations — it does not merge into the Postgres aggregation above. Generate and
migrate each datasource independently. The full step-by-step lives in the
-Use a second database recipe.
+Use a second database recipe.
{{ /comp }}
## Endpoints & ports
@@ -389,13 +390,13 @@ lane that matches what you're doing.
{
title: "Do — Use a second database",
body: "Task recipe: add an MSSQL or MySQL datasource beside Postgres with a driver adapter.",
- href: "/how-to/use-a-second-database/",
+ href: "/data-persistence/how-to/use-a-second-database/",
icon: "◆"
},
{
title: "Do — Database & migration",
body: "Task recipe: bring up Postgres with Aspire, then run db init → generate → seed → status → migrate.",
- href: "/how-to/database-migration/",
+ href: "/data-persistence/how-to/database-migration/",
icon: "◆"
},
{
diff --git a/docs/site/how-to/choose-a-queue-provider.md b/docs/site/data-persistence/how-to/choose-a-queue-provider.md
similarity index 97%
rename from docs/site/how-to/choose-a-queue-provider.md
rename to docs/site/data-persistence/how-to/choose-a-queue-provider.md
index abafebc0c..f0ad9911c 100644
--- a/docs/site/how-to/choose-a-queue-provider.md
+++ b/docs/site/data-persistence/how-to/choose-a-queue-provider.md
@@ -2,8 +2,8 @@
layout: layouts/base.vto
title: Choose a queue provider
templateEngine: [vento, md]
-prev: { label: "Use a second database", href: "/how-to/use-a-second-database/" }
-next: { label: "Tune the worker runtime", href: "/how-to/tune-worker-runtime/" }
+order: 104
+oldUrl: /how-to/choose-a-queue-provider/
---
# Choose a queue provider
@@ -12,7 +12,7 @@ next: { label: "Tune the worker runtime", href: "/how-to/tune-worker-runtime/" }
adapter, the zero-config Deno KV default, Redis, RabbitMQ (AMQP), and PostgreSQL — and how
to either let auto-discovery select one or pin one explicitly with `provider` +
`connection`. This is the *decision* recipe; for the enqueue/consume/cron mechanics see
-[Queue / KV / cron](/how-to/queue-kv-cron/).
+[Queue / KV / cron](/data-persistence/how-to/queue-kv-cron/).
A NetScript queue is provider-agnostic by design: the same `createQueue("jobs")` runs on an
in-memory adapter in a unit test, on Deno KV on your laptop, and on a real broker under
@@ -177,7 +177,7 @@ await queue.listen(async (message) => {
```
For CPU-bound work, prefer Web Workers over queue concurrency — see
-[Tune the worker runtime](/how-to/tune-worker-runtime/).
+[Tune the worker runtime](/background-processing/how-to/tune-worker-runtime/).
## Prove it end to end — same call site, two backends
@@ -256,4 +256,4 @@ For the full `QueueProvider` enum, the `QueueConnectionOptions` shape, `createPa
/ `ParallelQueueOptions`, the `MessageQueue` interface, and the `QueueError` hierarchy, see
{{ comp.xref({ key: "ref:queue" }) }}.
-{{ comp.nextPrev({ prev: { label: "Use a second database", href: "/how-to/use-a-second-database/" }, next: { label: "Tune the worker runtime", href: "/how-to/tune-worker-runtime/" } }) }}
+{{ comp.nextPrev({ prev: { label: "Use a second database", href: "/data-persistence/how-to/use-a-second-database/" }, next: { label: "Tune the worker runtime", href: "/background-processing/how-to/tune-worker-runtime/" } }) }}
diff --git a/docs/site/how-to/database-migration.md b/docs/site/data-persistence/how-to/database-migration.md
similarity index 97%
rename from docs/site/how-to/database-migration.md
rename to docs/site/data-persistence/how-to/database-migration.md
index bcc1b7633..2942cf7da 100644
--- a/docs/site/how-to/database-migration.md
+++ b/docs/site/data-persistence/how-to/database-migration.md
@@ -2,8 +2,8 @@
layout: layouts/base.vto
title: Database & migration
templateEngine: [vento, md]
-prev: { label: "Add a service", href: "/how-to/add-a-service/" }
-next: { label: "Queue / KV / cron", href: "/how-to/queue-kv-cron/" }
+order: 101
+oldUrl: /how-to/database-migration/
---
# Database & migration
@@ -64,7 +64,7 @@ Aspire is keeping alive in a second terminal.
] }) }}
If you scaffolded with `--no-aspire`, you are responsible for pointing the workspace at
-your own Postgres via `POSTGRES_URI` / `DATABASE_URL` (see [Deploy](/how-to/deploy/));
+your own Postgres via `POSTGRES_URI` / `DATABASE_URL` (see [Deploy](/orchestration-runtime/how-to/deploy/));
there is no AppHost to start, so you skip Step 2, but the migration commands in Step 3
are otherwise identical and talk to whatever database those variables resolve to.
@@ -238,7 +238,7 @@ import the generated client in a service or worker and read the rows the seed wr
Once the schema is live, the generated client is what every other capability reads and
writes through. Queue jobs persist their `message_queue` rows (the
-[PostgreSQL queue backend](/how-to/queue-kv-cron/) shares this same datasource), durable
+[PostgreSQL queue backend](/data-persistence/how-to/queue-kv-cron/) shares this same datasource), durable
sagas persist runtime state when configured with the Prisma store backend, and your
services query their models directly. The migration loop you just ran is the foundation the
rest of the workspace stands on.
@@ -251,8 +251,8 @@ rest of the workspace stands on.
{{ comp.card({ title: "Orchestration with Aspire", body: "Why the AppHost (aspire/apphost.mts) provisions Postgres and Redis, and how the resource graph fits together.", href: "/explanation/aspire/", icon: "◆" }) }}
-{{ comp.card({ title: "Queue / KV / cron", body: "The next recipe: use the KV and queue backends — including the PostgreSQL queue provider that shares this datasource.", href: "/how-to/queue-kv-cron/", icon: "→" }) }}
+{{ comp.card({ title: "Queue / KV / cron", body: "The next recipe: use the KV and queue backends — including the PostgreSQL queue provider that shares this datasource.", href: "/data-persistence/how-to/queue-kv-cron/", icon: "→" }) }}
-{{ comp.card({ title: "Deploy without Aspire", body: "Point the workspace at your own Postgres via POSTGRES_URI / DATABASE_URL using the --no-aspire escape hatch.", href: "/how-to/deploy/", icon: "→" }) }}
+{{ comp.card({ title: "Deploy without Aspire", body: "Point the workspace at your own Postgres via POSTGRES_URI / DATABASE_URL using the --no-aspire escape hatch.", href: "/orchestration-runtime/how-to/deploy/", icon: "→" }) }}
For the MySQL adapter surface, see [`prisma-adapter-mysql`](/reference/prisma-adapter-mysql/).
diff --git a/docs/site/data-persistence/how-to/index.md b/docs/site/data-persistence/how-to/index.md
new file mode 100644
index 000000000..858a0ab27
--- /dev/null
+++ b/docs/site/data-persistence/how-to/index.md
@@ -0,0 +1,12 @@
+---
+layout: layouts/base.vto
+title: Recipes
+templateEngine: [vento, md]
+order: 100
+---
+
+# Data & Persistence — recipes
+
+Task-oriented recipes for this area. Each one solves a single concrete problem and assumes you know
+the basics from the guides above it in the sidebar. The full cross-area catalog lives at
+[All how-to recipes]({{ "howto:index" |> xref |> url }}).
diff --git a/docs/site/how-to/queue-kv-cron.md b/docs/site/data-persistence/how-to/queue-kv-cron.md
similarity index 98%
rename from docs/site/how-to/queue-kv-cron.md
rename to docs/site/data-persistence/how-to/queue-kv-cron.md
index 68d2fa57e..8bcad4d78 100644
--- a/docs/site/how-to/queue-kv-cron.md
+++ b/docs/site/data-persistence/how-to/queue-kv-cron.md
@@ -2,8 +2,8 @@
layout: layouts/base.vto
title: Queue / KV / cron
templateEngine: [vento, md]
-prev: { label: "Database & migration", href: "/how-to/database-migration/" }
-next: { label: "Add OpenTelemetry", href: "/how-to/add-opentelemetry/" }
+order: 103
+oldUrl: /how-to/queue-kv-cron/
---
# Queue / KV / cron
@@ -350,9 +350,9 @@ adapter classes are documented in [`@netscript/cron`](/reference/cron/).
{
title: "Database & migration",
body: "The previous recipe — provision Postgres under Aspire and run the db init → generate → seed flow.",
- href: "/how-to/database-migration/",
+ href: "/data-persistence/how-to/database-migration/",
icon: "→"
}
] }) }}
-{{ comp.nextPrev({ prev: { label: "Database & migration", href: "/how-to/database-migration/" }, next: { label: "Add OpenTelemetry", href: "/how-to/add-opentelemetry/" } }) }}
+{{ comp.nextPrev({ prev: { label: "Database & migration", href: "/data-persistence/how-to/database-migration/" }, next: { label: "Add OpenTelemetry", href: "/observability/how-to/add-opentelemetry/" } }) }}
diff --git a/docs/site/how-to/use-a-second-database.md b/docs/site/data-persistence/how-to/use-a-second-database.md
similarity index 98%
rename from docs/site/how-to/use-a-second-database.md
rename to docs/site/data-persistence/how-to/use-a-second-database.md
index de510b489..121f08bab 100644
--- a/docs/site/how-to/use-a-second-database.md
+++ b/docs/site/data-persistence/how-to/use-a-second-database.md
@@ -2,8 +2,8 @@
layout: layouts/base.vto
title: Use a second database
templateEngine: [vento, md]
-prev: { label: "Expose OpenAPI & Scalar", href: "/how-to/expose-openapi-scalar/" }
-next: { label: "Choose a queue provider", href: "/how-to/choose-a-queue-provider/" }
+order: 102
+oldUrl: /how-to/use-a-second-database/
---
# Use a second database
@@ -19,7 +19,7 @@ file-backed SQLite primary instead. Everything below applies regardless of which
primary uses; the examples simply show the common Postgres-primary case.
NetScript's default scaffold gives you **one primary datasource** that every plugin
-aggregates its `.prisma` models into (see [Database & migration](/how-to/database-migration/)).
+aggregates its `.prisma` models into (see [Database & migration](/data-persistence/how-to/database-migration/)).
A second database is the opposite shape: a **separate** Prisma schema workspace with its own
`generate` output and its own migration history. It never merges into the primary aggregation.
@@ -265,4 +265,4 @@ The order is always: build the adapter, call getDriverAdapter(), pa
{{ comp.xref({ key: "explain:aspire", text: "Orchestration with Aspire" }) }} ·
{{ comp.xref({ key: "cap:kv-queues-cron", text: "KV, queues & cron — the Postgres queue backend" }) }}
-{{ comp.nextPrev({ prev: { label: "Expose OpenAPI & Scalar", href: "/how-to/expose-openapi-scalar/" }, next: { label: "Choose a queue provider", href: "/how-to/choose-a-queue-provider/" } }) }}
+{{ comp.nextPrev({ prev: { label: "Expose OpenAPI & Scalar", href: "/services-sdk/how-to/expose-openapi-scalar/" }, next: { label: "Choose a queue provider", href: "/data-persistence/how-to/choose-a-queue-provider/" } }) }}
diff --git a/docs/site/data-persistence/index.md b/docs/site/data-persistence/index.md
index cc7f99066..52abf1d5c 100644
--- a/docs/site/data-persistence/index.md
+++ b/docs/site/data-persistence/index.md
@@ -28,8 +28,17 @@ workspace resolves a second database.
{{ comp.cardsGrid({ columns: 3, cards: [
{ eyebrow: "Overview & Concepts", title: "Database and KV", body: "The persistence layer behind services, queues, and workflow state.", href: "/data-persistence/database/", icon: "O" },
{ eyebrow: "Quickstart", title: "Data contracts", body: "Follow the Storefront data step before service and saga work expand it.", href: "/tutorials/storefront/03-cart-contracts/", icon: "Q" },
- { eyebrow: "How-To", title: "Database and migration", body: "Initialize and migrate the primary datasource.", href: "/how-to/database-migration/", icon: "H" },
- { eyebrow: "How-To", title: "Second database", body: "Add and address another datasource from the workspace.", href: "/how-to/use-a-second-database/", icon: "H" },
+ { eyebrow: "How-To", title: "Database and migration", body: "Initialize and migrate the primary datasource.", href: "/data-persistence/how-to/database-migration/", icon: "H" },
+ { eyebrow: "How-To", title: "Second database", body: "Add and address another datasource from the workspace.", href: "/data-persistence/how-to/use-a-second-database/", icon: "H" },
{ eyebrow: "API Reference", title: "database", body: "Generated database package symbols.", href: "/reference/database/", icon: "R" },
{ eyebrow: "API Reference", title: "kv and Prisma adapter", body: "Generated KV and adapter package symbols.", href: "/reference/kv/", icon: "R" }
] }) }}
+
+## Learn, do, look up
+
+{{ comp.cardsGrid({ columns: 4, cards: [
+ { eyebrow: "Learn", title: "Storefront tutorial", body: "Cart contracts back a real database schema from chapter 3 on.", href: resolveXref("tut:storefront").href },
+ { eyebrow: "Do", title: "Recipes", body: "Task-oriented recipes for this area, one problem each.", href: "/data-persistence/how-to/" },
+ { eyebrow: "Look up", title: "`@netscript/database` reference", body: "Generated API reference. Related units: `kv`, `prisma-adapter-mysql`.", href: resolveXref("ref:database").href },
+ { eyebrow: "Understand", title: "Architecture", body: "The design rationale behind this pillar.", href: resolveXref("explain:architecture").href },
+] }) }}
diff --git a/docs/site/data-persistence/kv-queues-cron.md b/docs/site/data-persistence/kv-queues-cron.md
index cfc41f327..ffadd7af3 100644
--- a/docs/site/data-persistence/kv-queues-cron.md
+++ b/docs/site/data-persistence/kv-queues-cron.md
@@ -4,6 +4,7 @@ title: KV, queues & cron
templateEngine: [vento, md]
prev: { label: "Database & Prisma", href: "/data-persistence/database/" }
next: { label: "Telemetry & logging", href: "/observability/telemetry/" }
+order: 2
---
# KV, queues & cron
@@ -72,7 +73,7 @@ for you with no manual instrumentation.
{
title: "Do — Choose a queue provider",
body: "Recipe: pick between Deno KV, Redis, RabbitMQ, and PostgreSQL — and wire the Aspire-provisioned, local-fallback, and explicit-provider paths.",
- href: "/how-to/choose-a-queue-provider/",
+ href: "/data-persistence/how-to/choose-a-queue-provider/",
icon: "→"
},
{
@@ -353,7 +354,7 @@ for business-hours schedules. (5) always await scheduler.
{
title: "Do — Choose a queue provider",
body: "Task recipe: stand up each primitive in an existing workspace, with the Aspire-provisioned, local-fallback, and explicit-PostgreSQL paths spelled out.",
- href: "/how-to/choose-a-queue-provider/",
+ href: "/data-persistence/how-to/choose-a-queue-provider/",
icon: "◆"
},
{
diff --git a/docs/site/deno.lock b/docs/site/deno.lock
index c9ccaa43c..311255423 100644
--- a/docs/site/deno.lock
+++ b/docs/site/deno.lock
@@ -464,8 +464,10 @@
"https://deno.land/x/lume@v2.5.4/plugins/markdown.ts": "c7027605edee274762edb20f7040ccba6415c5fe656cc6e25ce91c448f467fd8",
"https://deno.land/x/lume@v2.5.4/plugins/modify_urls.ts": "137ba076b652f625b72de34692ea34b0fa5bbb5ba55cf7aa1ee789f2ce244bd5",
"https://deno.land/x/lume@v2.5.4/plugins/modules.ts": "d88ea1c1ab3bbbb501834f5409e5e00771a3dd4c9661b20e63445d1a80859257",
+ "https://deno.land/x/lume@v2.5.4/plugins/nav.ts": "d069569183b733866f8390943f3efda12e999985fc58390af5b7c418fc975c18",
"https://deno.land/x/lume@v2.5.4/plugins/pagefind.ts": "9b38d8ffb322d7fa4a4e9e8db36f6688835df00eb9901cebeb0b719ea65204fb",
"https://deno.land/x/lume@v2.5.4/plugins/paginate.ts": "7dfee977a205dfe0af33a3e406f73017badd2d4593cf27e5bd897da7ab12ba8a",
+ "https://deno.land/x/lume@v2.5.4/plugins/redirects.ts": "5c408b0de722c8a695f82e295625f881c4ed70b8e6a57a8aa5ecd3c5b4a9df31",
"https://deno.land/x/lume@v2.5.4/plugins/search.ts": "ff570560c6ca95598a1cbfb3a77611477ee7dbb53300bcc3ba14d18c9e5eba79",
"https://deno.land/x/lume@v2.5.4/plugins/toml.ts": "72c75546056e503a59752e33dc25542f2aa21d743bd47f498d722b97958212f5",
"https://deno.land/x/lume@v2.5.4/plugins/url.ts": "3718185697778f3b4dd17924d9d282d0a5a74030301e7fcae8a7f1b21f0ef9a9",
diff --git a/docs/site/how-to/build-a-validated-ingestion-queue.md b/docs/site/durable-workflows/how-to/build-a-validated-ingestion-queue.md
similarity index 83%
rename from docs/site/how-to/build-a-validated-ingestion-queue.md
rename to docs/site/durable-workflows/how-to/build-a-validated-ingestion-queue.md
index ca92960de..c62526e58 100644
--- a/docs/site/how-to/build-a-validated-ingestion-queue.md
+++ b/docs/site/durable-workflows/how-to/build-a-validated-ingestion-queue.md
@@ -2,8 +2,8 @@
layout: layouts/base.vto
title: Build a validated ingestion queue
templateEngine: [vento, md]
-prev: { label: "Build a server-validated form", href: "/how-to/build-a-server-validated-form/" }
-next: { label: "Publish a durable stream", href: "/how-to/publish-a-durable-stream/" }
+order: 101
+oldUrl: /how-to/build-a-validated-ingestion-queue/
---
# Build a validated ingestion queue
@@ -84,11 +84,11 @@ await queue.listen(async (message, context) => {
## Next steps
-- Compare providers in [Choose a queue provider](/how-to/choose-a-queue-provider/).
-- Use queues with KV and cron in [Queue / KV / cron](/how-to/queue-kv-cron/).
+- Compare providers in [Choose a queue provider](/data-persistence/how-to/choose-a-queue-provider/).
+- Use queues with KV and cron in [Queue / KV / cron](/data-persistence/how-to/queue-kv-cron/).
- Look up the full API in [queue reference](/reference/queue/).
{{ comp.nextPrev({
- prev: { label: "Build a server-validated form", href: "/how-to/build-a-server-validated-form/" },
- next: { label: "Publish a durable stream", href: "/how-to/publish-a-durable-stream/" }
+ prev: { label: "Build a server-validated form", href: "/web-layer/how-to/build-a-server-validated-form/" },
+ next: { label: "Publish a durable stream", href: "/durable-workflows/how-to/publish-a-durable-stream/" }
}) }}
diff --git a/docs/site/durable-workflows/how-to/index.md b/docs/site/durable-workflows/how-to/index.md
new file mode 100644
index 000000000..6769e47a8
--- /dev/null
+++ b/docs/site/durable-workflows/how-to/index.md
@@ -0,0 +1,12 @@
+---
+layout: layouts/base.vto
+title: Recipes
+templateEngine: [vento, md]
+order: 100
+---
+
+# Durable workflows — recipes
+
+Task-oriented recipes for this area. Each one solves a single concrete problem and assumes you know
+the basics from the guides above it in the sidebar. The full cross-area catalog lives at
+[All how-to recipes]({{ "howto:index" |> xref |> url }}).
diff --git a/docs/site/how-to/publish-a-durable-stream.md b/docs/site/durable-workflows/how-to/publish-a-durable-stream.md
similarity index 87%
rename from docs/site/how-to/publish-a-durable-stream.md
rename to docs/site/durable-workflows/how-to/publish-a-durable-stream.md
index 26b9fe35c..7c753de07 100644
--- a/docs/site/how-to/publish-a-durable-stream.md
+++ b/docs/site/durable-workflows/how-to/publish-a-durable-stream.md
@@ -2,8 +2,8 @@
layout: layouts/base.vto
title: Publish a durable stream
templateEngine: [vento, md]
-prev: { label: "Build a validated ingestion queue", href: "/how-to/build-a-validated-ingestion-queue/" }
-next: { label: "Restrict worker task permissions", href: "/how-to/restrict-worker-task-permissions/" }
+order: 102
+oldUrl: /how-to/publish-a-durable-stream/
---
# Publish a durable stream
@@ -107,6 +107,6 @@ addEventListener('unload', async () => {
- Look up the API in [streams reference](/reference/streams/).
{{ comp.nextPrev({
- prev: { label: "Build a validated ingestion queue", href: "/how-to/build-a-validated-ingestion-queue/" },
- next: { label: "Restrict worker task permissions", href: "/how-to/restrict-worker-task-permissions/" }
+ prev: { label: "Build a validated ingestion queue", href: "/durable-workflows/how-to/build-a-validated-ingestion-queue/" },
+ next: { label: "Restrict worker task permissions", href: "/background-processing/how-to/restrict-worker-task-permissions/" }
}) }}
diff --git a/docs/site/durable-workflows/index.md b/docs/site/durable-workflows/index.md
index c6b7dad0e..9281ba079 100644
--- a/docs/site/durable-workflows/index.md
+++ b/docs/site/durable-workflows/index.md
@@ -43,8 +43,17 @@ outcomes differ from a retry loop — read the
{{ comp.cardsGrid({ columns: 3, cards: [
{ eyebrow: "Overview & Concepts", title: "Durability model", body: "How saga state, trigger ingress, retries, and durable streams fit together.", href: "/explanation/durability-model/", icon: "O" },
{ eyebrow: "Quickstart", title: "Checkout saga", body: "Build a multi-step checkout flow in the Storefront tutorial.", href: "/tutorials/storefront/04-checkout-saga/", icon: "Q" },
- { eyebrow: "How-To", title: "Validated ingestion queue", body: "Validate incoming work before handing it to durable processing.", href: "/how-to/build-a-validated-ingestion-queue/", icon: "H" },
- { eyebrow: "How-To", title: "Durable stream", body: "Publish stream events for consumers.", href: "/how-to/publish-a-durable-stream/", icon: "H" },
+ { eyebrow: "How-To", title: "Validated ingestion queue", body: "Validate incoming work before handing it to durable processing.", href: "/durable-workflows/how-to/build-a-validated-ingestion-queue/", icon: "H" },
+ { eyebrow: "How-To", title: "Durable stream", body: "Publish stream events for consumers.", href: "/durable-workflows/how-to/publish-a-durable-stream/", icon: "H" },
{ eyebrow: "API Reference", title: "sagas", body: "Generated saga API symbols.", href: "/reference/sagas/", icon: "R" },
{ eyebrow: "API Reference", title: "triggers and streams", body: "Generated trigger and stream package symbols.", href: "/reference/triggers/", icon: "R" }
] }) }}
+
+## Learn, do, look up
+
+{{ comp.cardsGrid({ columns: 4, cards: [
+ { eyebrow: "Learn", title: "Storefront tutorial", body: "The checkout saga chapter models a multi-step flow with compensation.", href: resolveXref("tut:storefront").href },
+ { eyebrow: "Do", title: "Recipes", body: "Task-oriented recipes for this area, one problem each.", href: "/durable-workflows/how-to/" },
+ { eyebrow: "Look up", title: "`@netscript/sagas` reference", body: "Generated API reference. Related units: `triggers`, `streams`.", href: resolveXref("ref:sagas").href },
+ { eyebrow: "Understand", title: "The durability model", body: "The design rationale behind this pillar.", href: resolveXref("explain:durability-model").href },
+] }) }}
diff --git a/docs/site/durable-workflows/sagas.md b/docs/site/durable-workflows/sagas.md
index f1f4ab7d7..5cca960a3 100644
--- a/docs/site/durable-workflows/sagas.md
+++ b/docs/site/durable-workflows/sagas.md
@@ -4,6 +4,7 @@ title: Durable sagas
templateEngine: [vento, md]
prev: { label: "Background jobs", href: "/background-processing/workers/" }
next: { label: "Triggers & ingress", href: "/durable-workflows/triggers/" }
+order: 1
---
# Durable sagas
diff --git a/docs/site/durable-workflows/streams.md b/docs/site/durable-workflows/streams.md
index 354de3fc7..565d5112c 100644
--- a/docs/site/durable-workflows/streams.md
+++ b/docs/site/durable-workflows/streams.md
@@ -4,6 +4,7 @@ title: Durable streams
templateEngine: [vento, md]
prev: { label: "Triggers & ingress", href: "/durable-workflows/triggers/" }
next: { label: "Database & Prisma", href: "/data-persistence/database/" }
+order: 2
---
# Durable streams
@@ -92,7 +93,7 @@ report, which is as useful to a CLI doctor or a coding agent as it is to a test.
{
title: "Do — add the streams plugin",
body: "Public package install adds the stream plugin dependency and user-owned glue; local netscript-dev scaffolding supports contributor-source samples.",
- href: "/how-to/add-a-plugin/",
+ href: "/orchestration-runtime/how-to/add-a-plugin/",
icon: "◆"
},
{
diff --git a/docs/site/durable-workflows/triggers.md b/docs/site/durable-workflows/triggers.md
index 15ac1c921..4cb5c2ef4 100644
--- a/docs/site/durable-workflows/triggers.md
+++ b/docs/site/durable-workflows/triggers.md
@@ -4,6 +4,7 @@ title: Triggers & ingress
templateEngine: [vento, md]
prev: { label: "Durable sagas", href: "/durable-workflows/sagas/" }
next: { label: "Durable streams", href: "/durable-workflows/streams/" }
+order: 3
---
# Triggers & ingress
@@ -73,7 +74,7 @@ processor/ingress runtime (`@netscript/plugin-triggers-core/runtime`).
{
title: "Do — Add a plugin",
body: "The how-to recipe for public plugin package install and the local netscript-dev contributor path for source-backed trigger samples.",
- href: "/how-to/add-a-plugin/",
+ href: "/orchestration-runtime/how-to/add-a-plugin/",
icon: "◆"
}
] }) }}
diff --git a/docs/site/explanation/architecture.md b/docs/site/explanation/architecture.md
index 8f89361e8..0d073b59a 100644
--- a/docs/site/explanation/architecture.md
+++ b/docs/site/explanation/architecture.md
@@ -4,6 +4,7 @@ title: Architecture
templateEngine: [vento, md]
prev: { label: "Explanation", href: "/explanation/" }
next: { label: "Contracts & type flow", href: "/explanation/contracts/" }
+order: 1
---
# The NetScript architecture
diff --git a/docs/site/explanation/aspire.md b/docs/site/explanation/aspire.md
index 35920566b..b93c8ff7b 100644
--- a/docs/site/explanation/aspire.md
+++ b/docs/site/explanation/aspire.md
@@ -4,6 +4,7 @@ title: Orchestration with Aspire
templateEngine: [vento, md]
prev: { label: "Observability", href: "/explanation/observability/" }
next: { label: "Capabilities", href: "/capabilities/" }
+order: 7
---
# Orchestration with Aspire
diff --git a/docs/site/explanation/auth-model.md b/docs/site/explanation/auth-model.md
index e506c24e3..dcb81772b 100644
--- a/docs/site/explanation/auth-model.md
+++ b/docs/site/explanation/auth-model.md
@@ -4,17 +4,20 @@ title: The pure-backend auth model
templateEngine: [vento, md]
prev: { label: "The plugin system", href: "/explanation/plugin-system/" }
next: { label: "Durability model", href: "/explanation/durability-model/" }
+order: 4
---
# The pure-backend auth model
+*This essay is the **why** — the model and its trade-offs. The day-to-day *how* (guides, recipes, API) lives in **Build › [Identity & Access](/identity-access/)**.*
+
This page explains *what* NetScript authentication actually is, *why* it is designed as a
**pure-backend seam** rather than a built-in identity provider, and *how* a single typed port lets
you swap GitHub OAuth for WorkOS or better-auth without touching one line of application code. It is
understanding-oriented — read it to build the mental model before you wire authentication into a
project. When you want the headline API and endpoints, see the
[auth capability](/capabilities/auth/); when you want to do the task, follow
-[Add authentication](/how-to/add-authentication/); when you want exact exported symbols, follow
+[Add authentication](/identity-access/how-to/add-authentication/); when you want exact exported symbols, follow
[`reference/service/`](/reference/service/) and the auth adapter references it links to.
{{ comp callout { type: "important", title: "Alpha status" } }}
@@ -248,7 +251,7 @@ boundary, you own the logic) — authentication is just the identity-shaped inst
- **The capability:** [Authentication](/capabilities/auth/) — the headline API, the `auth-api`
service on :8094, the five endpoints, and the backend matrix at a glance.
-- **Do it:** [Add authentication](/how-to/add-authentication/) — add the `auth` plugin, choose a
+- **Do it:** [Add authentication](/identity-access/how-to/add-authentication/) — add the `auth` plugin, choose a
backend with `NETSCRIPT_AUTH_BACKEND`, run the migration, and wire provider env.
- **Related model:** [The plugin system](/explanation/plugin-system/) — why a thin plugin composes a
pure capability, the same shape this auth seam follows.
diff --git a/docs/site/explanation/contracts.md b/docs/site/explanation/contracts.md
index b2d0a03b1..7cc34f2d4 100644
--- a/docs/site/explanation/contracts.md
+++ b/docs/site/explanation/contracts.md
@@ -4,6 +4,7 @@ title: Contracts & type flow
templateEngine: [vento, md]
prev: { label: "Architecture", href: "/explanation/architecture/" }
next: { label: "The plugin system", href: "/explanation/plugin-system/" }
+order: 2
---
# Contracts & type flow
diff --git a/docs/site/explanation/durability-model.md b/docs/site/explanation/durability-model.md
index c21fd1589..8cdf97d72 100644
--- a/docs/site/explanation/durability-model.md
+++ b/docs/site/explanation/durability-model.md
@@ -4,10 +4,13 @@ title: Durability model
templateEngine: [vento, md]
prev: { label: "Auth model", href: "/explanation/auth-model/" }
next: { label: "Observability", href: "/explanation/observability/" }
+order: 5
---
# Durability model
+*This essay is the **why** — the model and its trade-offs. The day-to-day *how* (guides, recipes, API) lives in **Build › [Durable workflows](/durable-workflows/)**.*
+
This essay is **understanding-oriented**. It answers one question: *how does NetScript make
long-running, message-driven work survive a process restart?* It builds the mental model — what a
saga is, how its state is persisted, where that state physically lives, and how compensation and
diff --git a/docs/site/explanation/observability.md b/docs/site/explanation/observability.md
index a6a05a353..81828ded8 100644
--- a/docs/site/explanation/observability.md
+++ b/docs/site/explanation/observability.md
@@ -4,16 +4,19 @@ title: Observability
templateEngine: [vento, md]
prev: { label: "Durability model", href: "/explanation/durability-model/" }
next: { label: "Orchestration with Aspire", href: "/explanation/aspire/" }
+order: 6
---
# Observability
+*This essay is the **why** — the model and its trade-offs. The day-to-day *how* (guides, recipes, API) lives in **Build › [Observability](/observability/)**.*
+
This essay answers one question: how does NetScript make a *distributed, multi-process*
application observable, so that one logical operation reads as one story even though it crosses
HTTP, a queue, a saga, and a worker subprocess? The answer is a single idea applied everywhere —
**the trace context travels with the work** — wired into the framework boundaries so you inherit
it for free. Read this to build the mental model; to wire spans yourself, follow the
-[how-to: add OpenTelemetry](/how-to/add-opentelemetry/); for the headline API and ports, see the
+[how-to: add OpenTelemetry](/observability/how-to/add-opentelemetry/); for the headline API and ports, see the
{{ comp.xref({ key: "cap:telemetry" }) }} hub; for exact exported symbols, see
{{ comp.xref({ key: "ref:telemetry" }) }}.
@@ -272,7 +275,7 @@ the same span model.
## Where to go next
-- **Do it:** the [how-to: add OpenTelemetry](/how-to/add-opentelemetry/) walks adding a custom span,
+- **Do it:** the [how-to: add OpenTelemetry](/observability/how-to/add-opentelemetry/) walks adding a custom span,
structured logs, and `traceparent` propagation against a running service.
- **Hub:** the {{ comp.xref({ key: "cap:telemetry" }) }} hub covers the headline API and the
OTel-wired-into-boundaries model with the real endpoints; {{ comp.xref({ key: "cap:auth" }) }}
diff --git a/docs/site/explanation/plugin-system.md b/docs/site/explanation/plugin-system.md
index 2a279d017..e7dea879d 100644
--- a/docs/site/explanation/plugin-system.md
+++ b/docs/site/explanation/plugin-system.md
@@ -4,6 +4,7 @@ title: The plugin system
templateEngine: [vento, md]
prev: { label: "Contracts & type flow", href: "/explanation/contracts/" }
next: { label: "Auth model", href: "/explanation/auth-model/" }
+order: 3
---
# The plugin system
@@ -269,7 +270,7 @@ and topics through the same vocabulary — each running as its own isolated reso
## Where to go next
{{ comp.featureGrid({ items: [
- { title: "Add a first-party plugin", body: "The task-oriented recipe: scaffold, register, regenerate, verify.", href: "/how-to/add-a-plugin/", icon: "🔌" },
+ { title: "Add a first-party plugin", body: "The task-oriented recipe: scaffold, register, regenerate, verify.", href: "/orchestration-runtime/how-to/add-a-plugin/", icon: "🔌" },
{ title: "The auth model", body: "The five-unit core/adapter/plugin split — the model at its richest.", href: "/explanation/auth-model/", icon: "🔐" },
{ title: "Orchestration with Aspire", body: "How the AppHost turns manifests into running API and background resources.", href: "/explanation/aspire/", icon: "🧩" },
{ title: "Plugin authoring contract", body: "definePlugin(), the contribution axes, and the registry emitter.", href: "/reference/plugin/", icon: "📐" }
diff --git a/docs/site/glossary.md b/docs/site/glossary.md
index 527abac06..31cb722e2 100644
--- a/docs/site/glossary.md
+++ b/docs/site/glossary.md
@@ -37,7 +37,7 @@ These are the words you will meet first — in the [Quickstart](/quickstart/), t
{{ comp.apiTable({ caption: "A–C", rows: [
{ name: "AppHost", type: "aspire/apphost.mts", desc: "The Aspire orchestration entry point that boots your whole workspace — Postgres, Redis, services, and plugin processors — as one resource graph with a dashboard at https://localhost:18888. In NetScript the AppHost is a generated Node/TypeScript file (aspire/apphost.mts) driven by aspire.config.json and appsettings.json, not a dotnet project. You start it with cd aspire && aspire start, and it must be up before any netscript db command. See Explanation → Aspire and reference/aspire/." },
{ name: "archetype", type: "doctrine classification", desc: "The architectural category a package or plugin belongs to (for example a contract unit, an adapter, a runtime, or a plugin), which determines its public surface, quality gates, and allowed dependencies. Archetype is a framework-internal doctrine term; as an app author you mostly meet it indirectly through the shape of a scaffolded plugin. See Explanation → Plugin system." },
- { name: "Aspire", type: "aspire start", desc: "The .NET Aspire orchestration layer NetScript uses for local development. It provisions your database (Postgres is the recommended engine; or mysql / mssql via --db, each an Aspire container resource — sqlite is file-backed and adds no container) and Redis as Docker containers and wires every service and plugin processor together with health, logs, and OTLP traces — no manual docker compose. The escape hatch is netscript init --no-aspire for a leaner single-process loop. See Explanation → Aspire and How-to → Deploy." },
+ { name: "Aspire", type: "aspire start", desc: "The .NET Aspire orchestration layer NetScript uses for local development. It provisions your database (Postgres is the recommended engine; or mysql / mssql via --db, each an Aspire container resource — sqlite is file-backed and adds no container) and Redis as Docker containers and wires every service and plugin processor together with health, logs, and OTLP traces — no manual docker compose. The escape hatch is netscript init --no-aspire for a leaner single-process loop. See Explanation → Aspire and How-to → Deploy." },
{ name: "AuthBackendPort", type: "@netscript/plugin-auth-core/ports", desc: "The single seam every authentication backend implements — defined by @netscript/plugin-auth-core, it composes provider, session-store, crypto, and principal-mapper sub-ports plus an authenticate(request) method and an optional interactive flow. Backends are pure adapters behind this port; the host never special-cases a vendor. See Capabilities → Authentication and Explanation → Auth model." },
{ name: "AuthBackendOperationUnsupportedError", type: "typed capability boundary", desc: "The typed error a backend throws when you call an operation it does not implement — for example a session mutation on a stateless backend, or an interactive sign-in on a non-interactive one. It makes the single-active-backend capability matrix fail loud rather than silently no-op, so missing surface is a visible error, not a mystery. See Explanation → Auth model and the Capabilities → Auth export list." },
{ name: "AuthSession", type: "@netscript/plugin-auth-core/domain", desc: "The domain record for an authenticated session — id, user, account, and state (active | expired | revoked) — defined as a Zod-validated type in @netscript/plugin-auth-core. It is the durable shape the session store reads and writes and the entity the auth stream projects. See Explanation → Auth model and Capabilities → Authentication." },
@@ -59,7 +59,7 @@ These are the words you will meet first — in the [Quickstart](/quickstart/), t
{ name: "manifest", type: "plugins//mod.ts", desc: "A plugin's public face — its mod.ts — which exports the plugin object (for example workersPlugin or authPlugin), an inspector (inspectWorkers, inspectAuth), and the contribution types the host discovers. The composition root references each plugin by its manifest path, and a generated registry aggregates them. See Explanation → Plugin system." },
{ name: "oRPC", type: "@orpc/contract · @orpc/server", desc: "The contract-and-RPC toolkit (@orpc/*, pinned at ^1.14.6) that underpins NetScript's typed APIs: contracts are declared with oc.route().input().output(), implemented with implement(...), served by defineService / createService, and consumed by a fully typed client. Workers, sagas, the auth service, and triggers all expose oRPC (mounted under /api/rpc/*); triggers' only exception is the raw, HMAC-verifying webhook ingress endpoint. See Explanation → Contracts." },
{ name: "opaque session token (HMAC)", type: "createHmacSessionTokenCrypto(secret)", desc: "The default session-token scheme in the auth core — a WebCrypto HMAC-SHA256 token that carries no readable claims (opaque to the client) and is verified server-side against a secret. It is what the AuthSessionCryptoPort produces by default, so a stolen token reveals nothing and cannot be forged without the secret. See Explanation → Auth model and Capabilities → Auth." },
- { name: "plugin", type: "package + generated glue", desc: "An installable, thread-isolated capability. Public workspaces install plugin packages with commands such as netscript plugin install @netscript/plugin-workers, which add the dependency and emit user-owned glue/sample files that import the package; local-source contributor samples use deno run -A packages/cli/bin/netscript-dev.ts plugin install worker --name workers --samples. The plugin's contracts, services, runtime entrypoints, and Prisma schema stay in the installed package unless you are authoring or syncing full source. See Explanation → Plugin system and How-to → Add a plugin." },
+ { name: "plugin", type: "package + generated glue", desc: "An installable, thread-isolated capability. Public workspaces install plugin packages with commands such as netscript plugin install @netscript/plugin-workers, which add the dependency and emit user-owned glue/sample files that import the package; local-source contributor samples use deno run -A packages/cli/bin/netscript-dev.ts plugin install worker --name workers --samples. The plugin's contracts, services, runtime entrypoints, and Prisma schema stay in the installed package unless you are authoring or syncing full source. See Explanation → Plugin system and How-to → Add a plugin." },
{ name: "Principal", type: "@netscript/service/auth", desc: "The authenticated identity an authenticator resolves from a request — id, scopes, and a scheme ('api-key' | 'bearer' | 'trusted-header' | 'custom'). The auth-plugin backends map their sessions to a Principal with scheme: 'custom', so service-layer authz treats vendor-backed identities uniformly. See Explanation → Auth model." }
] }) }}
@@ -83,7 +83,7 @@ the data layer, the auth seams, and the moving parts under a running plugin.
{{ comp.apiTable({ caption: "Infrastructure, data & auth internals", rows: [
{ name: "appsettings.json", type: "infra config", desc: "The root infrastructure manifest the Aspire AppHost actually reads — declaring NetScript.Databases (the database engine — Postgres is the recommended engine, or mysql / mssql / sqlite selected at scaffold with --db — container mode, primary database), NetScript.Cache (the redis cache by default; garnet or deno-kv via --cache-backend), the services, and the per-plugin Workdirs. It is also where backend selections like sagas.store.backend and auth.backend can live. Note that netscript.config.ts's databases block is intentionally near-empty; the live DB/cache config lives here. See Explanation → Aspire." },
- { name: "auth backend", type: "@netscript/auth-*", desc: "A pure adapter that implements AuthBackendPort: kv-oauth (interactive OAuth/OIDC redirect flow, KV sessions — the only one exposing InteractiveFlowPort), workos (AuthKit sealed cookie, non-interactive), and better-auth (Prisma-backed, non-interactive). The plugin composes exactly one — see single-active-backend — and serves the unified auth oRPC surface on port :8094. See Explanation → Auth model and How-to → Add authentication." },
+ { name: "auth backend", type: "@netscript/auth-*", desc: "A pure adapter that implements AuthBackendPort: kv-oauth (interactive OAuth/OIDC redirect flow, KV sessions — the only one exposing InteractiveFlowPort), workos (AuthKit sealed cookie, non-interactive), and better-auth (Prisma-backed, non-interactive). The plugin composes exactly one — see single-active-backend — and serves the unified auth oRPC surface on port :8094. See Explanation → Auth model and How-to → Add authentication." },
{ name: "background processor", type: "bin/combined.ts", desc: "The separate, long-running process that executes a plugin's work, distinct from its HTTP API service. Workers run from bin/combined.ts; sagas run from src/runtime/saga-runner.ts; triggers run from src/runtime/trigger-processor.ts. The AppHost starts these as their own Aspire resources. See Explanation → Plugin system." },
{ name: "Garnet", type: "Cache.garnet (KV)", desc: "One of the Redis-compatible cache/KV backends Aspire can provision as a container alongside Postgres (the default is redis; select Garnet with --cache-backend garnet), used for executions, saga registry metadata, kv-oauth sessions, and Deno KV-backed state. See Capabilities → KV / queues / cron and reference/kv/." },
{ name: "OTel / observability", type: "@netscript/telemetry", desc: "OpenTelemetry tracing and structured logging wired into the framework. Job dispatch, execution, step events, progress, scheduler runs, and subprocess trace continuation emit real spans — traces show up in Aspire automatically (OTLP endpoint at http://localhost:4318). The one remaining gap is the scaffold createJobTools(ctx) handler helpers (trace.addEvent / withChildSpan / progress), which are still no-op stubs (tracked debt, fix planned) — for custom handler spans call @netscript/telemetry helpers directly. See Explanation → Observability and Capabilities → Telemetry." },
diff --git a/docs/site/how-to/index.md b/docs/site/how-to/index.md
index 38911a8a5..0bd41cdbb 100644
--- a/docs/site/how-to/index.md
+++ b/docs/site/how-to/index.md
@@ -1,99 +1,136 @@
---
layout: layouts/base.vto
-title: How-to guides
+title: All recipes
templateEngine: [vento, md]
prev: null
-next: { label: "Add a plugin", href: "/how-to/add-a-plugin/" }
+next: { label: "Add a plugin", href: "/orchestration-runtime/how-to/add-a-plugin/" }
+nav_hide: true
---
-How-to guides are **goal-first recipes**: each one starts from a concrete
-intent — *"I need to add a service,"* *"I need auth,"* *"I need this to deploy
-without Aspire"* — and gives you the shortest reliable path from that intent to a
-working, verified change. They assume you already have a NetScript workspace and
-know the basics; they do **not** re-teach the framework.
+# All recipes
-If NetScript is new to you, start with the [tutorials](/tutorials/) — they build
-one continuous application from zero. For exact API signatures, use the
-[reference](/reference/). For the concepts *behind* a task — why services are
-contracts-first, what "durable" means, how Aspire wires dependencies — read the
-[explanation](/explanation/) pages. Each recipe links back to the capability hub
-and reference that go deeper.
+Recipes live inside their pillar in the sidebar — each area's **Recipes** section
+sits below the guides that teach the basics it assumes. This page is the
+cross-area index: every recipe in the docs, grouped by pillar, so you can scan
+the whole catalog in one place. Each one is goal-first — it starts from a
+concrete intent and ends with a command that proves the change works.
{{ comp callout { tone: "info", title: "One prerequisite spans almost every recipe" } }}
Anything that touches Postgres, Redis/Garnet, or a plugin service expects Aspire
to be running first. From your workspace: cd aspire && aspire start
brings up the dependencies and the dashboard on https://localhost:18888
-before any netscript db command or service call. The
-recipes call this out where it matters, but it is the single most common missing
-step.
+before any netscript db command or service call.
+The recipes call this out where it matters, but it is the single most common
+missing step.
{{ /comp }}
-## Build & extend a workspace
-
-These recipes add capabilities to an existing workspace and verify the wiring.
-Each emits the relevant workspace-owned glue, samples, or service files and ends with a
-command you can run to confirm it works.
-
-{{ comp.featureGrid({ items: [
- { title: "Add a plugin", body: "Install a first-party plugin through the public package flow, or use the local netscript-dev path for contributor-source samples. Emits user-owned glue, regenerates the registry, and verifies the service answers on its port.", href: "/how-to/add-a-plugin/" },
- { title: "Add a service", body: "Stand up a new typed oRPC service: define an @orpc/contract + zod contract, implement() the handlers, serve it with defineService(...) one-shot or createService(...).serve() fluent, and confirm it answers on /api/rpc/*.", href: "/how-to/add-a-service/" },
- { title: "Add authentication", body: "Add the official auth plugin (auth-api on :8094, five endpoints under /api/v1/auth/*). Pick one active backend via NETSCRIPT_AUTH_BACKEND — kv-oauth (interactive, default), WorkOS, or better-auth — run the auth.prisma migration, and sign in.", href: "/how-to/add-authentication/" },
- { title: "Database & migration", body: "Initialize, generate, seed, and inspect the Postgres schema: netscript db init --name init → db generate → db seed → db status. Requires aspire start first so Postgres is provisioned.", href: "/how-to/database-migration/" }
-] }) }}
-
-## Set up the developer environment
-
-Recipes for the toolchain around a NetScript workspace: editor intelligence, orchestration, and
-runtime setup that should behave the same on every device.
-
-{{ comp.featureGrid({ items: [
- { title: "Deno LSP code intelligence", body: "Install the Claude Code Deno LSP plugin, enable the LSP tool globally, and keep go-to-definition, hover, references, symbols, and diagnostics aligned across CLI, VS Code, and Zed.", href: "/how-to/deno-lsp-code-intelligence/" },
- { title: "Deploy locally with Aspire", body: "Run the full local resource graph from the generated Aspire AppHost: dashboard, infrastructure, services, plugin APIs, and background processors.", href: "/how-to/deploy-local-aspire/" }
-] }) }}
-
-## Wire primitives & observability
-
-Recipes for the shared building blocks every plugin leans on — queues, KV, cron,
-and the OpenTelemetry traces that make them visible in the Aspire dashboard.
-
-{{ comp.featureGrid({ items: [
- { title: "Queue / KV / cron", body: "Use the reactive KV store, the durable queue (four backends — RabbitMQ, Redis, Deno KV, and explicit-provider PostgreSQL), and cron schedules. Covers --unstable-kv and the auto-discovery order vs. an explicit provider:'postgres'.", href: "/how-to/queue-kv-cron/" },
- { title: "Add OpenTelemetry", body: "Emit custom spans and structured logs with @netscript/telemetry helpers, propagate traceparent across services, and read the traces that land in the Aspire dashboard. Worker job dispatch/execution traces are already real and automatic.", href: "/how-to/add-opentelemetry/" }
-] }) }}
-
-## Ship the UI & deploy
-
-Recipes for the front end and for taking a workspace to production — including
-the Aspire-free portability path.
-
-{{ comp.featureGrid({ items: [
- { title: "Customize the Fresh UI", body: "Bring in and own the dashboard UI with the ui:init / ui:add tasks. The scaffold uses copy-source ownership — the components land in your workspace, so you edit them directly rather than depending on a hidden package.", href: "/how-to/customize-fresh-ui/" },
- { title: "Build a durable chat", body: "Wire an AI chat onto a Fresh route whose transcript survives reload and reconnect via a durable session stream; hydrate the fresh-ui chat components; and add one server-side tool.", href: "/how-to/build-a-durable-chat/" },
- { title: "Deploy", body: "The portability and config story: the raw deno task entry points behind each service, plus the --no-aspire escape hatch when you provision Postgres and Redis (or Garnet) yourself. Docker, Compose, and Linux targets are config-only scaffolding today, not runnable deploy verbs — for a first-class hosted path, see Deploy to Deno Deploy.", href: "/how-to/deploy/" },
- { title: "Deploy to Deno Deploy", body: "Push a workspace to Deno Deploy with the first-class netscript deploy deno-deploy plan | up | down | status | logs command: preflight the unstable-API guard, push a preview, promote to prod, and read status and logs.", href: "/how-to/deploy-deno-deploy/" },
- { title: "Author a plugin", body: "Advanced: build a custom plugin from scratch. Defines the scaffold.plugin.json provider kind, the manifest exports, and the mod.ts contract the host discovers — the same shape the first-party plugins use.", href: "/how-to/author-a-plugin/" }
-] }) }}
-
-## How a recipe is shaped
-
-Every how-to page follows the same contract so you always know where to look:
-
-- **Goal** — one sentence stating exactly what you will have when you finish.
-- **Prerequisites** — the workspace state and running dependencies the recipe
- assumes (almost always including a live `aspire start`).
-- **Steps** — added-lines code blocks, annotated with the file path they belong
- in, using the public `netscript ` command form throughout.
-- **Production pitfalls** — the caveats that bite in real deployments, stated
- plainly rather than glossed over.
-- **See also** — the capability hub, reference page, and related recipes that
- take the topic further.
-
-{{ comp callout { tone: "note", title: "Pick by intent, not by feature name" } }}
-If you are not sure which recipe you want, name the outcome first. "Users
-must sign in" → Add authentication.
-"This event should fan out to a background job" →
-Add a plugin (triggers + workers).
-"It has to run where there is no Aspire" →
-Deploy. The recipes are deliberately small and
-composable; most real features chain two or three of them.
-{{ /comp }}
+## Web Layer
+
+- [Customize the Fresh UI]({{ "howto:customize-fresh-ui" |> xref |> url }}) —
+ bring the dashboard components into your workspace with `ui:init` / `ui:add`
+ and edit them directly.
+- [Build a server-validated form]({{ "howto:build-a-server-validated-form" |> xref |> url }}) —
+ route-bound form state, server validation, mutation, and success handling in
+ one typed page definition with `definePage().withForm()`.
+- [Build a desktop frontend]({{ "howto:build-a-desktop-frontend" |> xref |> url }}) —
+ one Fresh frontend that runs as an ordinary browser app and gains native
+ capabilities on the desktop.
+
+## Services & SDK
+
+- [Add a service]({{ "howto:add-a-service" |> xref |> url }}) — define a typed
+ contract, implement the handlers, and confirm the service answers on
+ `/api/rpc/*`.
+- [Discover services]({{ "howto:discover-services" |> xref |> url }}) — call
+ another plugin's or workspace member's typed service without hardcoding its
+ address.
+- [Expose OpenAPI & Scalar]({{ "howto:expose-openapi-scalar" |> xref |> url }}) —
+ publish an OpenAPI document and browsable Scalar docs for a service.
+
+## Background jobs
+
+- [Run a polyglot task]({{ "howto:run-a-polyglot-task" |> xref |> url }}) —
+ define a non-TypeScript script (Python, shell, .NET, any executable) as a
+ task with a permission sandbox and run it through the executor.
+- [Tune the worker runtime]({{ "howto:tune-worker-runtime" |> xref |> url }}) —
+ trade throughput against isolation with concurrency, runner mode, per-task
+ permissions, and timeouts/retries.
+- [Restrict worker task permissions]({{ "howto:restrict-worker-task-permissions" |> xref |> url }}) —
+ give every Deno task explicit permissions; an omitted permission object
+ compiles to `--allow-all`.
+- [Add a task runtime adapter]({{ "howto:add-a-task-runtime-adapter" |> xref |> url }}) —
+ advanced: add a custom runtime adapter to the built-in task executor.
+
+## Durable workflows
+
+- [Build a validated ingestion queue]({{ "howto:build-a-validated-ingestion-queue" |> xref |> url }}) —
+ a typed queue whose messages are schema-checked before they enter the queue
+ and again before a consumer handles them.
+- [Publish a durable stream]({{ "howto:publish-a-durable-stream" |> xref |> url }}) —
+ let server-side state be subscribed to by browsers and other consumers
+ through a durable stream service.
+
+## AI & Agents
+
+- [Build a durable chat]({{ "howto:build-a-durable-chat" |> xref |> url }}) —
+ an AI chat on a Fresh route whose transcript survives reload and reconnect,
+ with one server-side tool.
+
+## Data & Persistence
+
+- [Database & migration]({{ "howto:database-migration" |> xref |> url }}) —
+ initialize, generate, seed, and inspect the Postgres schema with the
+ `netscript db` commands.
+- [Queue / KV / cron]({{ "howto:queue-kv-cron" |> xref |> url }}) — the reactive
+ KV store, the durable queue, and cron schedules, including `--unstable-kv`.
+- [Choose a queue provider]({{ "howto:choose-a-queue-provider" |> xref |> url }}) —
+ pick the right queue backend, and either let auto-discovery select one or pin
+ one explicitly.
+- [Use a second database]({{ "howto:use-a-second-database" |> xref |> url }}) —
+ add a second Postgres, or a MySQL/SQL Server instance, alongside the default
+ database.
+
+## Identity & Access
+
+- [Add authentication]({{ "howto:add-authentication" |> xref |> url }}) — install
+ the official auth plugin, pick one active backend via
+ `NETSCRIPT_AUTH_BACKEND`, migrate, and sign in.
+
+## Orchestration & Runtime
+
+- [Add a plugin]({{ "howto:add-a-plugin" |> xref |> url }}) — install a
+ first-party plugin, regenerate the registry, and verify the service answers.
+- [Deploy locally with Aspire]({{ "howto:deploy-local-aspire" |> xref |> url }}) —
+ run the full local resource graph from the generated Aspire AppHost.
+- [Deploy]({{ "howto:deploy" |> xref |> url }}) — the portability story: raw
+ `deno task` entry points and the `--no-aspire` escape hatch when you provision
+ dependencies yourself.
+- [Deploy to Deno Deploy]({{ "howto:deploy-deno-deploy" |> xref |> url }}) —
+ push a preview, promote to prod, and read status and logs with the
+ first-class deploy command.
+- [Graceful shutdown]({{ "howto:graceful-shutdown" |> xref |> url }}) — drain
+ in-flight requests and jobs, run teardown hooks, and close connections on
+ `SIGINT`/`SIGTERM`.
+- [Roll out runtime overrides]({{ "howto:roll-out-runtime-overrides" |> xref |> url }}) —
+ change a deployed behavior without rebuilding the workspace.
+- [Author a plugin]({{ "howto:author-a-plugin" |> xref |> url }}) — advanced:
+ build a custom plugin with the same manifest and `mod.ts` contract the
+ first-party plugins use.
+- [Deno LSP code intelligence]({{ "howto:deno-lsp-code-intelligence" |> xref |> url }}) —
+ keep go-to-definition, hover, and diagnostics aligned across CLI and editors.
+
+## Observability
+
+- [Add OpenTelemetry]({{ "howto:add-opentelemetry" |> xref |> url }}) — emit
+ custom spans and structured logs, propagate `traceparent`, and read the
+ traces in the Aspire dashboard.
+
+---
+
+Not sure which recipe you want? Name the outcome first. *"Users must sign in"* →
+[Add authentication]({{ "howto:add-authentication" |> xref |> url }}).
+*"This event should fan out to a background job"* →
+[Add a plugin]({{ "howto:add-a-plugin" |> xref |> url }}).
+*"It has to run where there is no Aspire"* →
+[Deploy]({{ "howto:deploy" |> xref |> url }}). The recipes are deliberately
+small and composable; most real features chain two or three of them.
diff --git a/docs/site/identity-access/auth.md b/docs/site/identity-access/auth.md
index 558dd44ee..1e74eabc6 100644
--- a/docs/site/identity-access/auth.md
+++ b/docs/site/identity-access/auth.md
@@ -4,6 +4,7 @@ title: Authentication
templateEngine: [vento, md]
prev: { label: "Fresh UI & design", href: "/web-layer/fresh-ui/" }
next: null
+order: 1
---
# Authentication
@@ -42,7 +43,7 @@ or OIDC provider — a typed /api/v1/auth/* surface your s
UI can call to sign users in, resolve the current session, and sign out. The default
kv-oauth backend gives you a complete interactive redirect flow with KV-stored
sessions and an opaque HMAC-signed session token. To wire it into a workspace step by step, see
-Add authentication; to understand the pure-backend
+Add authentication; to understand the pure-backend
port model and why only one backend is active, see
The authentication model.
{{ /comp }}
@@ -106,7 +107,7 @@ and how `InteractiveFlowPort` gates the redirect flow — is in
{
title: "Do — Add authentication",
body: "Task recipe: add the auth plugin, set NETSCRIPT_AUTH_BACKEND, run the auth.prisma migration, and wire kv-oauth with a provider preset.",
- href: "/how-to/add-authentication/",
+ href: "/identity-access/how-to/add-authentication/",
icon: "◆"
},
{
@@ -296,7 +297,7 @@ better-auth-shaped models — `User` → `auth_users`, `Session` → `auth_sessi
`Account` → `auth_accounts`, `Verification` → `auth_verifications`. These tables back the
better-auth adapter; `kv-oauth` keeps its sessions in Deno KV, and WorkOS is stateless. As with
every plugin schema, you bring the tables to life by running the database workflow **after Aspire
-is up** — see [Database migrations](/how-to/database-migration/).
+is up** — see [Database migrations](/data-persistence/how-to/database-migration/).
The plugin also emits five durable `auth.*` runtime events through the durable-streams runtime —
the `AUTH_STREAM_EVENT_TYPES`: `auth.signin.started`, `auth.signin.failed`,
@@ -375,7 +376,7 @@ adapters now have dedicated generated reference pages.
{
title: "Do — Add authentication",
body: "Task recipe: add the auth plugin, set NETSCRIPT_AUTH_BACKEND, run the auth.prisma migration, and wire kv-oauth with a provider preset.",
- href: "/how-to/add-authentication/",
+ href: "/identity-access/how-to/add-authentication/",
icon: "◆"
},
{
diff --git a/docs/site/identity-access/better-auth-plugins.md b/docs/site/identity-access/better-auth-plugins.md
index ddcd86495..65787fb08 100644
--- a/docs/site/identity-access/better-auth-plugins.md
+++ b/docs/site/identity-access/better-auth-plugins.md
@@ -2,6 +2,7 @@
layout: layouts/base.vto
title: better-auth plugins
templateEngine: [vento, md]
+order: 2
---
# better-auth plugins
diff --git a/docs/site/how-to/add-authentication.md b/docs/site/identity-access/how-to/add-authentication.md
similarity index 99%
rename from docs/site/how-to/add-authentication.md
rename to docs/site/identity-access/how-to/add-authentication.md
index c0cbe89c3..5ce355e39 100644
--- a/docs/site/how-to/add-authentication.md
+++ b/docs/site/identity-access/how-to/add-authentication.md
@@ -2,8 +2,8 @@
layout: layouts/base.vto
title: Add authentication
templateEngine: [vento, md]
-prev: { label: "Author a plugin", href: "/how-to/author-a-plugin/" }
-next: null
+order: 101
+oldUrl: /how-to/add-authentication/
---
# Add authentication
@@ -157,7 +157,7 @@ netscript db seed # optional seed data
netscript db status # confirm the migration is applied
```
-See [Run a database migration](/how-to/database-migration/) for the full DB workflow and the
+See [Run a database migration](/data-persistence/how-to/database-migration/) for the full DB workflow and the
Aspire-up dependency.
## Step 4 — Configure the provider and secrets
@@ -354,7 +354,7 @@ aspire start first.
{{ comp.card({
title: "Run a database migration",
body: "The full db init / generate / seed / status workflow that applies auth.prisma.",
- href: "/how-to/database-migration/",
+ href: "/data-persistence/how-to/database-migration/",
icon: "+"
}) }}
diff --git a/docs/site/identity-access/how-to/index.md b/docs/site/identity-access/how-to/index.md
new file mode 100644
index 000000000..b561c099d
--- /dev/null
+++ b/docs/site/identity-access/how-to/index.md
@@ -0,0 +1,12 @@
+---
+layout: layouts/base.vto
+title: Recipes
+templateEngine: [vento, md]
+order: 100
+---
+
+# Identity & Access — recipes
+
+Task-oriented recipes for this area. Each one solves a single concrete problem and assumes you know
+the basics from the guides above it in the sidebar. The full cross-area catalog lives at
+[All how-to recipes]({{ "howto:index" |> xref |> url }}).
diff --git a/docs/site/identity-access/index.md b/docs/site/identity-access/index.md
index 064de5f6e..ffdaf2689 100644
--- a/docs/site/identity-access/index.md
+++ b/docs/site/identity-access/index.md
@@ -26,8 +26,17 @@ failure modes the contract closes off — is on the
{ eyebrow: "Capability", title: "Authentication", body: "The auth plugin story: one active backend, five endpoints, fail-loud typed boundaries, and the salt-gated audit surface.", href: "/identity-access/auth/", icon: "C" },
{ eyebrow: "Overview & Concepts", title: "Auth model", body: "Principal, session, backend, and authorization vocabulary.", href: "/explanation/auth-model/", icon: "O" },
{ eyebrow: "Quickstart", title: "Workspace auth", body: "Add authentication in the Workspace tutorial.", href: "/tutorials/workspace/02-auth/", icon: "Q" },
- { eyebrow: "How-To", title: "Add authentication", body: "Wire authentication into a generated workspace.", href: "/how-to/add-authentication/", icon: "H" },
+ { eyebrow: "How-To", title: "Add authentication", body: "Wire authentication into a generated workspace.", href: "/identity-access/how-to/add-authentication/", icon: "H" },
{ eyebrow: "How-To", title: "better-auth plugins", body: "Mount better-auth plugins through a typed passthrough; bearer and jwt run as-is, table-backed and interactive plugins carry caveats.", href: "/identity-access/better-auth-plugins/", icon: "H" },
{ eyebrow: "API Reference", title: "auth backends", body: "Generated symbols for auth, better-auth, kv-oauth, and WorkOS packages.", href: "/reference/auth/", icon: "R" },
{ eyebrow: "API Reference", title: "plugin auth", body: "Generated plugin-auth package symbols.", href: "/reference/plugin-auth/", icon: "R" }
] }) }}
+
+## Learn, do, look up
+
+{{ comp.cardsGrid({ columns: 4, cards: [
+ { eyebrow: "Learn", title: "Workspace tutorial", body: "Auth, workspace data, and route authorization in one track.", href: resolveXref("tut:workspace").href },
+ { eyebrow: "Do", title: "Recipes", body: "Task-oriented recipes for this area, one problem each.", href: "/identity-access/how-to/" },
+ { eyebrow: "Look up", title: "`@netscript/auth` reference", body: "Generated API reference. Related units: `auth-better-auth`, `auth-kv-oauth`, `auth-workos`, `plugin-auth`.", href: resolveXref("ref:auth").href },
+ { eyebrow: "Understand", title: "The auth model", body: "The design rationale behind this pillar.", href: resolveXref("explain:auth-model").href },
+] }) }}
diff --git a/docs/site/how-to/add-opentelemetry.md b/docs/site/observability/how-to/add-opentelemetry.md
similarity index 99%
rename from docs/site/how-to/add-opentelemetry.md
rename to docs/site/observability/how-to/add-opentelemetry.md
index 591123517..797aa9cf2 100644
--- a/docs/site/how-to/add-opentelemetry.md
+++ b/docs/site/observability/how-to/add-opentelemetry.md
@@ -2,8 +2,8 @@
layout: layouts/base.vto
title: Add OpenTelemetry
templateEngine: [vento, md]
-prev: { "label": "Queue / KV / cron", "href": "/how-to/queue-kv-cron/" }
-next: { "label": "Customize Fresh UI", "href": "/how-to/customize-fresh-ui/" }
+order: 101
+oldUrl: /how-to/add-opentelemetry/
---
# Add OpenTelemetry
diff --git a/docs/site/observability/how-to/index.md b/docs/site/observability/how-to/index.md
new file mode 100644
index 000000000..aef58c6de
--- /dev/null
+++ b/docs/site/observability/how-to/index.md
@@ -0,0 +1,12 @@
+---
+layout: layouts/base.vto
+title: Recipes
+templateEngine: [vento, md]
+order: 100
+---
+
+# Observability — recipes
+
+Task-oriented recipes for this area. Each one solves a single concrete problem and assumes you know
+the basics from the guides above it in the sidebar. The full cross-area catalog lives at
+[All how-to recipes]({{ "howto:index" |> xref |> url }}).
diff --git a/docs/site/observability/index.md b/docs/site/observability/index.md
index 0cfa7a4f8..a60907bf5 100644
--- a/docs/site/observability/index.md
+++ b/docs/site/observability/index.md
@@ -17,7 +17,7 @@ or connect package-level telemetry to the running system.
{{ comp.cardsGrid({ columns: 3, cards: [
{ eyebrow: "Overview & Concepts", title: "Telemetry model", body: "Trace context and logging across services, workers, sagas, and orchestration.", href: "/explanation/observability/", icon: "O" },
{ eyebrow: "Quickstart", title: "Turn on tracing", body: "Run aspire start, trigger a job, and watch the runtime's automatic spans land in the dashboard — the lowest-effort path to a first trace.", href: "/observability/telemetry/", icon: "Q" },
- { eyebrow: "How-To", title: "Add OpenTelemetry", body: "Wire OTel into a workspace.", href: "/how-to/add-opentelemetry/", icon: "H" },
+ { eyebrow: "How-To", title: "Add OpenTelemetry", body: "Wire OTel into a workspace.", href: "/observability/how-to/add-opentelemetry/", icon: "H" },
{ eyebrow: "API Reference", title: "telemetry", body: "Generated telemetry package symbols.", href: "/reference/telemetry/", icon: "R" },
{ eyebrow: "API Reference", title: "logger", body: "Generated logger package symbols.", href: "/reference/logger/", icon: "R" }
] }) }}
@@ -44,3 +44,12 @@ learn the model:
{{ comp.xref({ key: "ref:logger", text: "logger" }) }} references, and the
[telemetry convention](/reference/telemetry/convention/) (span naming, SpanKind, the `netscript.*`
attribute rules).
+
+## Learn, do, look up
+
+{{ comp.cardsGrid({ columns: 4, cards: [
+ { eyebrow: "Learn", title: "Trace the request model", body: "Follow one request through services, jobs, and streams in the core-concepts tour.", href: resolveXref("concept:concepts").href },
+ { eyebrow: "Do", title: "Recipes", body: "Task-oriented recipes for this area, one problem each.", href: "/observability/how-to/" },
+ { eyebrow: "Look up", title: "`@netscript/telemetry` reference", body: "Generated API reference. Related units: `logger`.", href: resolveXref("ref:telemetry").href },
+ { eyebrow: "Understand", title: "Observability", body: "The design rationale behind this pillar.", href: resolveXref("explain:observability").href },
+] }) }}
diff --git a/docs/site/observability/telemetry.md b/docs/site/observability/telemetry.md
index b97e95422..63b4b82dc 100644
--- a/docs/site/observability/telemetry.md
+++ b/docs/site/observability/telemetry.md
@@ -4,6 +4,7 @@ title: Telemetry & logging
templateEngine: [vento, md]
prev: { label: "KV, queues & cron", href: "/data-persistence/kv-queues-cron/" }
next: { label: "Fresh UI & design", href: "/web-layer/fresh-ui/" }
+order: 1
---
# Telemetry & logging
@@ -44,7 +45,7 @@ orchestrator — they are not separate processes you start by hand. Run
command (Aspire provisions Postgres and Redis first), then open the dashboard URL printed in
the console (https://localhost:18888, with a one-time auth token). Until Aspire is
running there is no :18888 surface to view traces or logs on. See
-Database & migration for the full startup order.
+Database & migration for the full startup order.
{{ /comp }}
## The story: one grouped trace, end to end
@@ -100,7 +101,7 @@ NetScript-owned keys under the single proprietary root `netscript.*` — correla
{
title: "Do — Add OpenTelemetry",
body: "Task-oriented recipe: lean on the automatic job spans, add custom spans via @netscript/telemetry withChildSpan, propagate traceparent across services and subprocesses, and point the OTLP export (:4318) at the dashboard.",
- href: "/how-to/add-opentelemetry/",
+ href: "/observability/how-to/add-opentelemetry/",
icon: "◆"
},
{
@@ -485,7 +486,7 @@ This hub is intentionally thin — the full generated API lives in the reference
{
title: "Do — Add OpenTelemetry",
body: "Task-oriented recipe: lean on the automatic job spans, add custom spans via @netscript/telemetry withChildSpan, propagate traceparent across services and subprocesses, and point the OTLP export (:4318) at the dashboard.",
- href: "/how-to/add-opentelemetry/",
+ href: "/observability/how-to/add-opentelemetry/",
icon: "◆"
},
{
diff --git a/docs/site/orchestration-runtime/cli-scaffold.md b/docs/site/orchestration-runtime/cli-scaffold.md
index 78ca4ea7e..ede4a6c47 100644
--- a/docs/site/orchestration-runtime/cli-scaffold.md
+++ b/docs/site/orchestration-runtime/cli-scaffold.md
@@ -4,6 +4,7 @@ title: CLI and Scaffold
templateEngine: [vento, md]
prev: { label: "Orchestration & Runtime", href: "/orchestration-runtime/" }
next: { label: "Runtime configuration", href: "/orchestration-runtime/runtime-config/" }
+order: 1
---
# CLI & scaffold
diff --git a/docs/site/how-to/add-a-plugin.md b/docs/site/orchestration-runtime/how-to/add-a-plugin.md
similarity index 97%
rename from docs/site/how-to/add-a-plugin.md
rename to docs/site/orchestration-runtime/how-to/add-a-plugin.md
index 579236345..d2bfb58a5 100644
--- a/docs/site/how-to/add-a-plugin.md
+++ b/docs/site/orchestration-runtime/how-to/add-a-plugin.md
@@ -2,12 +2,8 @@
layout: layouts/base.vto
title: Add a plugin
templateEngine: [vento, md]
-prev:
- label: How-to guides
- href: /how-to/
-next:
- label: Add a service
- href: /how-to/add-a-service/
+order: 106
+oldUrl: /how-to/add-a-plugin/
---
# Add a plugin
@@ -102,7 +98,7 @@ dependency, emits a user-owned auth/mod.ts glue barrel, registers t
auth-api service on port 8094, and contributes its package-provided
Prisma models. The active backend is selected at runtime with
NETSCRIPT_AUTH_BACKEND (default kv-oauth). See
-Configure authentication for the backend setup.
+Configure authentication for the backend setup.
{{ /comp }}
## Step 2 — Add the plugin
@@ -258,8 +254,8 @@ Run `netscript plugin --help` for the complete, version-accurate command set.
### Where to go next
- **Build on the plugin you just added.** Next up:
- [Add a service](/how-to/add-a-service/) to give the plugin something to call, or
- [Configure authentication](/how-to/add-authentication/) if you added the auth plugin.
+ [Add a service](/services-sdk/how-to/add-a-service/) to give the plugin something to call, or
+ [Configure authentication](/identity-access/how-to/add-authentication/) if you added the auth plugin.
- **Understand the model.** Read [Plugin architecture](/explanation/plugin-system/) for
the design behind installable capabilities, ports, and runtime registration.
- **Browse capabilities.** The [capabilities](/capabilities/) section maps each plugin to the
diff --git a/docs/site/how-to/author-a-plugin.md b/docs/site/orchestration-runtime/how-to/author-a-plugin.md
similarity index 98%
rename from docs/site/how-to/author-a-plugin.md
rename to docs/site/orchestration-runtime/how-to/author-a-plugin.md
index 020372b8f..d30422796 100644
--- a/docs/site/how-to/author-a-plugin.md
+++ b/docs/site/orchestration-runtime/how-to/author-a-plugin.md
@@ -2,8 +2,8 @@
layout: layouts/base.vto
title: Author a plugin
templateEngine: [vento, md]
-prev: { label: "Deploy to Deno Deploy", href: "/how-to/deploy-deno-deploy/" }
-next: { label: "Add authentication", href: "/how-to/add-authentication/" }
+order: 107
+oldUrl: /how-to/author-a-plugin/
---
# Author a plugin
@@ -11,7 +11,7 @@ next: { label: "Add authentication", href: "/how-to/add-authentication/" }
**Scope.** This recipe shows how to author a *new* custom plugin from scratch — its canonical
location, the manifest it exports through `definePlugin(...)`, the contribution shape the kernel
reads, and the generated registry that makes those contributions discoverable at runtime. It is the
-advanced companion to [Add a first-party plugin](/how-to/add-a-plugin/), which only *installs* one
+advanced companion to [Add a first-party plugin](/orchestration-runtime/how-to/add-a-plugin/), which only *installs* one
of the four official plugins (`workers`, `sagas`, `triggers`, `streams`). If you just want a worker
or a saga, install it there. Come here when you need a capability NetScript does not ship.
@@ -346,7 +346,7 @@ When your plugin grows beyond a single package — a core seam plus swappable ad
The pattern to copy: a **core package owns the port** (`AuthBackendPort`), each **adapter is pure**
(implements the port, declares no service), and the **plugin under `plugins//`** composes one
active adapter into a single service and registry. That keeps adapters swappable and the kernel-facing
-manifest small. Build the full thing in [Add authentication](/how-to/add-authentication/) — that page
+manifest small. Build the full thing in [Add authentication](/identity-access/how-to/add-authentication/) — that page
is the concrete walkthrough for the auth plugin specifically.
{{ comp callout { type: "note", title: "Alpha package pins" } }}
@@ -390,14 +390,14 @@ declare it with .withDependencies({...}) on the manifest and in
{{ comp.card({
title: "Add a first-party plugin",
body: "Install one of the four official plugins instead of writing your own.",
- href: "/how-to/add-a-plugin/",
+ href: "/orchestration-runtime/how-to/add-a-plugin/",
icon: "+"
}) }}
{{ comp.card({
title: "Add authentication",
body: "Build the multi-package auth plugin — the production exemplar for a core seam plus swappable backends.",
- href: "/how-to/add-authentication/",
+ href: "/identity-access/how-to/add-authentication/",
icon: "→"
}) }}
diff --git a/docs/site/how-to/deno-lsp-code-intelligence.md b/docs/site/orchestration-runtime/how-to/deno-lsp-code-intelligence.md
similarity index 97%
rename from docs/site/how-to/deno-lsp-code-intelligence.md
rename to docs/site/orchestration-runtime/how-to/deno-lsp-code-intelligence.md
index 9c2a4d189..5a8be798b 100644
--- a/docs/site/how-to/deno-lsp-code-intelligence.md
+++ b/docs/site/orchestration-runtime/how-to/deno-lsp-code-intelligence.md
@@ -2,8 +2,8 @@
layout: layouts/base.vto
title: Deno LSP code intelligence in Claude Code
templateEngine: [vento, md]
-prev: { label: "How-to guides", href: "/how-to/" }
-next: { label: "Deploy locally with Aspire", href: "/how-to/deploy-local-aspire/" }
+order: 108
+oldUrl: /how-to/deno-lsp-code-intelligence/
---
# Deno LSP code intelligence in Claude Code
diff --git a/docs/site/how-to/deploy-deno-deploy.md b/docs/site/orchestration-runtime/how-to/deploy-deno-deploy.md
similarity index 97%
rename from docs/site/how-to/deploy-deno-deploy.md
rename to docs/site/orchestration-runtime/how-to/deploy-deno-deploy.md
index 2cd7dc848..a228d1e0e 100644
--- a/docs/site/how-to/deploy-deno-deploy.md
+++ b/docs/site/orchestration-runtime/how-to/deploy-deno-deploy.md
@@ -2,8 +2,8 @@
layout: layouts/base.vto
title: Deploy to Deno Deploy
templateEngine: [vento, md]
-prev: { label: "Deploy", href: "/how-to/deploy/" }
-next: { label: "Author a plugin", href: "/how-to/author-a-plugin/" }
+order: 103
+oldUrl: /how-to/deploy-deno-deploy/
---
# Deploy to Deno Deploy
@@ -23,7 +23,7 @@ config-to-argv wiring, nothing more.
netscript deploy deno-deploy is fully wired and runnable. The Docker, Compose, and
Linux/systemd targets are config schema only — there is no runnable
netscript deploy docker|compose|linux verb yet. For those targets you assemble the
-deployment yourself from the primitives in the Deploy recipe.
+deployment yourself from the primitives in the Deploy recipe.
{{ /comp }}
## Before you start
@@ -185,7 +185,7 @@ Many NetScript runtime plugins (workers, sagas, triggers) require Deno KV and th
`--unstable-kv`. Those background processors are not a fit for a single Deno Deploy isolate — deploy a
KV-free entrypoint (for example a Fresh app or a stateless oRPC service) here, and run the KV-backed
processors on infrastructure that permits `--unstable-kv`, as covered in the
-[Deploy](/how-to/deploy/) recipe.
+[Deploy](/orchestration-runtime/how-to/deploy/) recipe.
## Troubleshooting
@@ -207,19 +207,19 @@ processors on infrastructure that permits `--unstable-kv`, as covered in the
{
title: "Deploy (containers & bare metal)",
body: "The manual path: deployable units from appsettings.json, backing services, per-process deno run commands, and the --no-aspire escape hatch.",
- href: "/how-to/deploy/",
+ href: "/orchestration-runtime/how-to/deploy/",
icon: "◆"
},
{
title: "Deploy locally with Aspire",
body: "Run the whole graph on one machine under the Aspire AppHost before you ship anything remote.",
- href: "/how-to/deploy-local-aspire/",
+ href: "/orchestration-runtime/how-to/deploy-local-aspire/",
icon: "◎"
},
{
title: "Add OpenTelemetry",
body: "Wire spans and traceparent propagation to your OTLP collector so a deployed process is observable.",
- href: "/how-to/add-opentelemetry/",
+ href: "/observability/how-to/add-opentelemetry/",
icon: "≋"
}
] }) }}
diff --git a/docs/site/how-to/deploy-local-aspire.md b/docs/site/orchestration-runtime/how-to/deploy-local-aspire.md
similarity index 97%
rename from docs/site/how-to/deploy-local-aspire.md
rename to docs/site/orchestration-runtime/how-to/deploy-local-aspire.md
index e0bda9505..6c90bfe8e 100644
--- a/docs/site/how-to/deploy-local-aspire.md
+++ b/docs/site/orchestration-runtime/how-to/deploy-local-aspire.md
@@ -2,8 +2,8 @@
layout: layouts/base.vto
title: Deploy locally with Aspire
templateEngine: [vento, md]
-prev: { label: "Graceful shutdown", href: "/how-to/graceful-shutdown/" }
-next: { label: "How-to guides", href: "/how-to/" }
+order: 102
+oldUrl: /how-to/deploy-local-aspire/
---
# Deploy locally with Aspire
@@ -11,7 +11,7 @@ next: { label: "How-to guides", href: "/how-to/" }
**Goal:** run your whole NetScript workspace on one machine under .NET Aspire — scaffold the
AppHost, bring up the resource graph (Postgres, Redis, every service and background processor),
and watch it from the Aspire dashboard. This is the **local** orchestration companion to the
-[Deploy](/how-to/deploy/) recipe (which covers shipping to a remote target); for *why* the
+[Deploy](/orchestration-runtime/how-to/deploy/) recipe (which covers shipping to a remote target); for *why* the
AppHost works the way it does, read [Orchestration with Aspire](/explanation/aspire/).
{{ comp callout { type: "important", title: "The order is: scaffold → orchestrate → database" } }}
@@ -198,4 +198,4 @@ a Deno workspace problem.
- **Exact symbols + full port map:** {{ comp.xref({ key: "ref:aspire", text: "the Aspire reference" }) }}
and the {{ comp.xref({ key: "cli:reference", text: "CLI reference" }) }}.
-{{ comp.nextPrev({ prev: { label: "Graceful shutdown", href: "/how-to/graceful-shutdown/" }, next: { label: "How-to guides", href: "/how-to/" } }) }}
+{{ comp.nextPrev({ prev: { label: "Graceful shutdown", href: "/orchestration-runtime/how-to/graceful-shutdown/" }, next: { label: "How-to guides", href: "/how-to/" } }) }}
diff --git a/docs/site/how-to/deploy.md b/docs/site/orchestration-runtime/how-to/deploy.md
similarity index 98%
rename from docs/site/how-to/deploy.md
rename to docs/site/orchestration-runtime/how-to/deploy.md
index 0ae0cc4c4..69d4b46e9 100644
--- a/docs/site/how-to/deploy.md
+++ b/docs/site/orchestration-runtime/how-to/deploy.md
@@ -2,8 +2,8 @@
layout: layouts/base.vto
title: Deploy
templateEngine: [vento, md]
-prev: { label: "Build a durable chat", href: "/how-to/build-a-durable-chat/" }
-next: { label: "Deploy to Deno Deploy", href: "/how-to/deploy-deno-deploy/" }
+order: 101
+oldUrl: /how-to/deploy/
---
# Deploy a NetScript workspace
@@ -51,7 +51,7 @@ The scaffolded workflows cover the deploy surfaces that ship today:
(netscript deploy build). They are intentionally starter pipelines: fill in repository
secrets, GitHub environment protection, host credentials, and target-specific configuration before
you treat them as production release jobs. See
-Deploy to Deno Deploy for the managed-platform command
+Deploy to Deno Deploy for the managed-platform command
reference.
{{ /comp }}
@@ -63,7 +63,7 @@ There is now a first-class, runnable managed-platform path:
preflight guard and deploy.targets['deno-deploy'] config. Aspire-backed targets are
also routed: netscript deploy docker|compose|kubernetes|azure-aca|azure-app-service|azure-aks|cloud-run <op>
delegates to the target adapter. See
-Deploy to Deno Deploy for Deno Deploy details.
+Deploy to Deno Deploy for Deno Deploy details.
{{ /comp }}
## Before you start
@@ -181,7 +181,7 @@ Postgres is provisioned by Aspire. So the order is always:
db seed. Running a DB command with no Postgres reachable — for example in an
isolated CI container — fails fast. In production, point the same commands at your managed
Postgres via POSTGRES_URI or DATABASE_URL instead of relying on
-Aspire. See Database migration for the full sequence.
+Aspire. See Database migration for the full sequence.
{{ /comp }}
## Step 3 — Provision backing services
@@ -419,7 +419,7 @@ deployment is live.
{
title: "Deploy to Deno Deploy",
body: "The one runnable managed-platform path: netscript deploy deno-deploy plan/up/status/logs/down, the unstable-API guard, and deploy.targets['deno-deploy'] config.",
- href: "/how-to/deploy-deno-deploy/",
+ href: "/orchestration-runtime/how-to/deploy-deno-deploy/",
icon: "◆"
},
{
@@ -431,13 +431,13 @@ deployment is live.
{
title: "Database migration",
body: "Run db init → generate → seed → status against Postgres — and why aspire start comes first.",
- href: "/how-to/database-migration/",
+ href: "/data-persistence/how-to/database-migration/",
icon: "▣"
},
{
title: "Add OpenTelemetry",
body: "Wire spans and traceparent propagation to your OTLP collector in production.",
- href: "/how-to/add-opentelemetry/",
+ href: "/observability/how-to/add-opentelemetry/",
icon: "≋"
}
] }) }}
diff --git a/docs/site/how-to/graceful-shutdown.md b/docs/site/orchestration-runtime/how-to/graceful-shutdown.md
similarity index 97%
rename from docs/site/how-to/graceful-shutdown.md
rename to docs/site/orchestration-runtime/how-to/graceful-shutdown.md
index 7fe144851..6f574d0a5 100644
--- a/docs/site/how-to/graceful-shutdown.md
+++ b/docs/site/orchestration-runtime/how-to/graceful-shutdown.md
@@ -2,8 +2,8 @@
layout: layouts/base.vto
title: Graceful shutdown
templateEngine: [vento, md]
-prev: { label: "Run a polyglot task", href: "/how-to/run-a-polyglot-task/" }
-next: { label: "Deploy locally with Aspire", href: "/how-to/deploy-local-aspire/" }
+order: 105
+oldUrl: /how-to/graceful-shutdown/
---
# Graceful shutdown
@@ -241,7 +241,7 @@ entrypoint by hand.
{
title: "Do — Tune the worker runtime",
body: "Recipe: concurrency, retry, and the runtime knobs that decide how much in-flight work a drain has to wait on.",
- href: "/how-to/tune-worker-runtime/",
+ href: "/background-processing/how-to/tune-worker-runtime/",
icon: "◆"
},
{
@@ -252,4 +252,4 @@ entrypoint by hand.
}
] }) }}
-{{ comp.nextPrev({ prev: { label: "Run a polyglot task", href: "/how-to/run-a-polyglot-task/" }, next: { label: "Deploy locally with Aspire", href: "/how-to/deploy-local-aspire/" } }) }}
+{{ comp.nextPrev({ prev: { label: "Run a polyglot task", href: "/background-processing/how-to/run-a-polyglot-task/" }, next: { label: "Deploy locally with Aspire", href: "/orchestration-runtime/how-to/deploy-local-aspire/" } }) }}
diff --git a/docs/site/orchestration-runtime/how-to/index.md b/docs/site/orchestration-runtime/how-to/index.md
new file mode 100644
index 000000000..4a5b79a08
--- /dev/null
+++ b/docs/site/orchestration-runtime/how-to/index.md
@@ -0,0 +1,12 @@
+---
+layout: layouts/base.vto
+title: Recipes
+templateEngine: [vento, md]
+order: 100
+---
+
+# Orchestration & Runtime — recipes
+
+Task-oriented recipes for this area. Each one solves a single concrete problem and assumes you know
+the basics from the guides above it in the sidebar. The full cross-area catalog lives at
+[All how-to recipes]({{ "howto:index" |> xref |> url }}).
diff --git a/docs/site/how-to/roll-out-runtime-overrides.md b/docs/site/orchestration-runtime/how-to/roll-out-runtime-overrides.md
similarity index 87%
rename from docs/site/how-to/roll-out-runtime-overrides.md
rename to docs/site/orchestration-runtime/how-to/roll-out-runtime-overrides.md
index f1c6fe960..9360ce937 100644
--- a/docs/site/how-to/roll-out-runtime-overrides.md
+++ b/docs/site/orchestration-runtime/how-to/roll-out-runtime-overrides.md
@@ -2,8 +2,8 @@
layout: layouts/base.vto
title: Roll out runtime overrides
templateEngine: [vento, md]
-prev: { label: "Deploy locally with Aspire", href: "/how-to/deploy-local-aspire/" }
-next: { label: "Add a task runtime adapter", href: "/how-to/add-a-task-runtime-adapter/" }
+order: 104
+oldUrl: /how-to/roll-out-runtime-overrides/
---
# Roll out runtime overrides
@@ -106,11 +106,11 @@ The next watcher reload sees the previous version and calls `onChange` again.
## Next steps
-- Tune worker behavior with [Tune the worker runtime](/how-to/tune-worker-runtime/).
-- Deploy the process with [Deploy](/how-to/deploy/).
+- Tune worker behavior with [Tune the worker runtime](/background-processing/how-to/tune-worker-runtime/).
+- Deploy the process with [Deploy](/orchestration-runtime/how-to/deploy/).
- Look up the package surface in [runtime-config reference](/reference/runtime-config/).
{{ comp.nextPrev({
- prev: { label: "Deploy locally with Aspire", href: "/how-to/deploy-local-aspire/" },
- next: { label: "Add a task runtime adapter", href: "/how-to/add-a-task-runtime-adapter/" }
+ prev: { label: "Deploy locally with Aspire", href: "/orchestration-runtime/how-to/deploy-local-aspire/" },
+ next: { label: "Add a task runtime adapter", href: "/background-processing/how-to/add-a-task-runtime-adapter/" }
}) }}
diff --git a/docs/site/orchestration-runtime/index.md b/docs/site/orchestration-runtime/index.md
index 50bd8af02..1dfc1bab2 100644
--- a/docs/site/orchestration-runtime/index.md
+++ b/docs/site/orchestration-runtime/index.md
@@ -26,9 +26,9 @@ workspace up, change runtime overrides, or understand how plugin contributions b
{ eyebrow: "Story", title: "CLI & scaffold", body: "One command to a complete workspace — and why generated conventions save agent turns.", href: "/orchestration-runtime/cli-scaffold/", icon: "S" },
{ eyebrow: "Story", title: "Runtime configuration", body: "Typed project config plus hot-reloadable operator overrides.", href: "/orchestration-runtime/runtime-config/", icon: "S" },
{ eyebrow: "Quickstart", title: "Run the workspace", body: "Scaffold and start a workspace from the quickstart.", href: "/quickstart/", icon: "Q" },
- { eyebrow: "How-To", title: "Deploy locally with Aspire", body: "Use Aspire to bring up local resources.", href: "/how-to/deploy-local-aspire/", icon: "H" },
- { eyebrow: "How-To", title: "Runtime overrides", body: "Roll out configuration overrides across resources.", href: "/how-to/roll-out-runtime-overrides/", icon: "H" },
- { eyebrow: "How-To", title: "Plugins", body: "Add or author a plugin contribution.", href: "/how-to/add-a-plugin/", icon: "H" },
+ { eyebrow: "How-To", title: "Deploy locally with Aspire", body: "Use Aspire to bring up local resources.", href: "/orchestration-runtime/how-to/deploy-local-aspire/", icon: "H" },
+ { eyebrow: "How-To", title: "Runtime overrides", body: "Roll out configuration overrides across resources.", href: "/orchestration-runtime/how-to/roll-out-runtime-overrides/", icon: "H" },
+ { eyebrow: "How-To", title: "Plugins", body: "Add or author a plugin contribution.", href: "/orchestration-runtime/how-to/add-a-plugin/", icon: "H" },
{ eyebrow: "API Reference", title: "aspire and runtime config", body: "Generated symbols for AppHost, config, runtime-config, plugin, and CLI units.", href: "/reference/aspire/", icon: "R" }
] }) }}
@@ -50,3 +50,12 @@ New here, start with the concept, then the practical wiring, then the reference:
dashboard side of the same graph — the spans and logs `aspire start` collects.
- **Look up exact symbols:** {{ comp.xref({ key: "ref:aspire", text: "the Aspire reference" }) }} and
the {{ comp.xref({ key: "cli:reference", text: "CLI reference" }) }}.
+
+## Learn, do, look up
+
+{{ comp.cardsGrid({ columns: 4, cards: [
+ { eyebrow: "Learn", title: "Quickstart", body: "From `netscript init` to a running, orchestrated workspace.", href: resolveXref("concept:quickstart").href },
+ { eyebrow: "Do", title: "Recipes", body: "Task-oriented recipes for this area, one problem each.", href: "/orchestration-runtime/how-to/" },
+ { eyebrow: "Look up", title: "`@netscript/aspire` reference", body: "Generated API reference. Related units: `cli`, `config`, `runtime-config`, `plugin`.", href: resolveXref("ref:aspire").href },
+ { eyebrow: "Understand", title: "Orchestration with Aspire", body: "The design rationale behind this pillar.", href: resolveXref("explain:aspire").href },
+] }) }}
diff --git a/docs/site/orchestration-runtime/runtime-config.md b/docs/site/orchestration-runtime/runtime-config.md
index b53e3bb1c..12e6ac060 100644
--- a/docs/site/orchestration-runtime/runtime-config.md
+++ b/docs/site/orchestration-runtime/runtime-config.md
@@ -4,6 +4,7 @@ title: Runtime configuration
templateEngine: [vento, md]
prev: { label: "Polyglot tasks", href: "/background-processing/polyglot-tasks/" }
next: { label: "Capabilities", href: "/capabilities/" }
+order: 2
---
# Runtime configuration
@@ -56,7 +57,7 @@ deliberately distinct: `@netscript/config` owns the build-time contract, and
{
title: "Do — Choose a queue provider",
body: "A config-driven decision recipe: pick and wire a queue backend through project config.",
- href: "/how-to/choose-a-queue-provider/",
+ href: "/data-persistence/how-to/choose-a-queue-provider/",
icon: "◆"
}
] }) }}
diff --git a/docs/site/quickstart.vto b/docs/site/quickstart.vto
index 47c006c37..524835fc3 100644
--- a/docs/site/quickstart.vto
+++ b/docs/site/quickstart.vto
@@ -104,7 +104,7 @@ service, and so on) — on the strict happy path above, the four steps are suffi
On the default path, Aspire is step 2 of running your app: cd aspire && aspire start
provisions Postgres and Redis first. Any netscript db command (init, generate, seed) runs
after Aspire is up, because it needs that database to exist. See
-Database migrations for the full flow.
+Database migrations for the full flow.
{{ /comp }}
## 3. Start it
diff --git a/docs/site/quickstart/aspire.md b/docs/site/quickstart/aspire.md
new file mode 100644
index 000000000..07c6d0997
--- /dev/null
+++ b/docs/site/quickstart/aspire.md
@@ -0,0 +1,85 @@
+---
+layout: layouts/base.vto
+title: "Aspire quickstart"
+templateEngine: [vento, md]
+---
+
+# Aspire quickstart
+
+A NetScript workspace is never one process — it is a Fresh app, oRPC services, plugin APIs,
+background processors, a database, and a cache. Aspire is how we make that whole fleet start with
+**one command**, wired together, with a real dashboard from the first run. This page is the
+shortest path to seeing it; the [main Quickstart](/quickstart/) covers installing the CLI and the
+scaffold in more detail.
+
+{{ comp callout { type: "important", title: "Alpha" } }}
+NetScript is alpha software and the API is subject to change. Pin versions in real projects.
+{{ /comp }}
+
+## What you get
+
+- **One workspace, one command up.** `aspire start` boots the database, the cache, every service,
+ every plugin API, and every background processor — in dependency order, no docker-compose to
+ babysit.
+- **Multi-resource wiring, resolved for you.** Connection strings and neighbour endpoints are
+ computed and injected as environment variables before each process starts, so nothing has to
+ discover anything at runtime.
+- **The Aspire dashboard.** Live resource list, per-process console logs, and distributed traces
+ in one place — `aspire start` prints its URL and a one-time login token.
+- **A TypeScript AppHost — not .NET authoring.** The orchestrator entry point is a generated
+ TypeScript program at `aspire/apphost.mts`, running on an isolated Node runtime inside
+ `aspire/` so it never leaks into your Deno workspace. You write no C#.
+
+{{ comp callout { type: "note", title: "Prerequisites" } }}
+Deno 2.x and the netscript CLI
+(install steps in the Quickstart), the external
+.NET Aspire CLI, and a
+running Docker daemon — Aspire provisions Postgres and Redis as local containers.
+{{ /comp }}
+
+## The commands
+
+Scaffold a workspace, then bring it up. The Aspire layer lives in its own `aspire/` folder;
+restore its SDK modules once, then start:
+
+```bash
+netscript init my-app --db postgres
+
+cd my-app/aspire
+aspire restore # one-time: downloads the AppHost SDK modules
+aspire start # boots Postgres + Redis + services, prints the dashboard URL
+```
+
+When boot settles, open the dashboard URL `aspire start` printed (conventionally
+`https://localhost:18888`) and paste the login token. Every resource, its logs, and its traces are
+one click away.
+
+{{ comp callout { type: "note", title: "Database commands come after" } }}
+netscript db init, db generate, and db seed run from the
+workspace root only once aspire start is up — they provision the
+database through the running AppHost. With no Aspire up there is no Postgres for them to
+reach.
+{{ /comp }}
+
+## Prefer no orchestration?
+
+Aspire is the default, not a requirement. Scaffold with `--no-aspire` to skip the orchestration
+layer entirely — no `aspire/` folder, no dashboard — and start the Fresh app directly:
+
+```bash
+netscript init my-app --db postgres --no-aspire
+deno task --cwd apps/dashboard dev
+```
+
+You take over infrastructure and wiring yourself: bring your own Postgres and cache, hand each
+process its connection strings. When that trade is the right call — and what exactly you give
+up — is covered in [Orchestration with Aspire]({{ "explain:aspire" |> xref |> url }}).
+
+## Where next
+
+- **Step-by-step recipe:** [Deploy locally with Aspire]({{ "howto:deploy-local-aspire" |> xref |> url }}) —
+ the full local flow, including the database sequence and first-run footguns.
+- **Why it works this way:** [Orchestration with Aspire]({{ "explain:aspire" |> xref |> url }}) —
+ the AppHost, plugin contributions, and the resource graph.
+- **Exact symbols and the port map:** [the Aspire reference]({{ "ref:aspire" |> xref |> url }}) and
+ the [CLI reference]({{ "cli:reference" |> xref |> url }}).
diff --git a/docs/site/reference/ai/skills.md b/docs/site/reference/ai/skills.md
index 6e0c34776..dbce3d327 100644
--- a/docs/site/reference/ai/skills.md
+++ b/docs/site/reference/ai/skills.md
@@ -26,7 +26,7 @@ of three ready-made skills that teach an agent the NetScript vocabulary.
`netscript agent init` installs three first-party skills on the **Claude Code host path**
(under `.claude/skills/`, alongside `.mcp.json` and the marked `AGENTS.md` section); the
VS Code host path writes `.vscode/mcp.json` only, with no skills. They share one vocabulary with the
-`netscript` CLI and the [NetScript MCP tools](/capabilities/agent-tooling/#tool-catalog),
+`netscript` CLI and the [NetScript MCP tools](/ai/agent-tooling/#tool-catalog),
so an agent routes to a workflow and then reaches for the matching CLI verb or MCP tool.
| Skill | Role |
@@ -37,7 +37,7 @@ so an agent routes to a workflow and then reaches for the matching CLI verb or M
These are the skills named in the bundle manifest and installed as an atomic set;
re-running `netscript agent init` is idempotent. See
-[Agent tooling](/capabilities/agent-tooling/) for the CLI × skills × MCP combo.
+[Agent tooling](/ai/agent-tooling/) for the CLI × skills × MCP combo.
```ts
import {
diff --git a/docs/site/reference/cli/commands.md b/docs/site/reference/cli/commands.md
index 70831baef..cb6765db6 100644
--- a/docs/site/reference/cli/commands.md
+++ b/docs/site/reference/cli/commands.md
@@ -49,7 +49,7 @@ the [quickstart](/quickstart/); every flag is:
## `agent` — install and run agent tooling
`netscript agent` installs and runs the shared CLI × skills × MCP tooling. See
-[Agent tooling](/capabilities/agent-tooling/) for the mental model and the
+[Agent tooling](/ai/agent-tooling/) for the mental model and the
[`@netscript/mcp` reference](/reference/mcp/) for the server's tool surface.
| Command | Description |
@@ -134,7 +134,7 @@ the [CLI reference](/cli-reference/#plugins). The full group also carries:
| `netscript plugin auth session revoke ` | Revoke an auth session by id. Flag: `--auth-url `. |
The backends selectable here are the same ones read at runtime by
-`NETSCRIPT_AUTH_BACKEND` — see [add authentication](/how-to/add-authentication/).
+`NETSCRIPT_AUTH_BACKEND` — see [add authentication](/identity-access/how-to/add-authentication/).
## `service` — extended verbs
@@ -192,7 +192,7 @@ also lists, updates, and removes copied registry items:
Because Fresh UI is copy-source, `ui:update` only touches registry files you have not
modified — your edits are never overwritten. See
-[customize Fresh UI](/how-to/customize-fresh-ui/).
+[customize Fresh UI](/web-layer/how-to/customize-fresh-ui/).
## `deploy` — cloud and container targets
diff --git a/docs/site/reference/kv/index.md b/docs/site/reference/kv/index.md
index 9c7251443..609def5d7 100644
--- a/docs/site/reference/kv/index.md
+++ b/docs/site/reference/kv/index.md
@@ -139,7 +139,7 @@ A reusable contract harness for adapter authors. Re-exports the shared `KvStore`
## See it live
-- **How-to:** [Queue / KV / cron](/how-to/queue-kv-cron/) — read, write, and watch a KV store with
+- **How-to:** [Queue / KV / cron](/data-persistence/how-to/queue-kv-cron/) — read, write, and watch a KV store with
these symbols in a running workspace.
- **Concept:** [KV, queues & cron](/data-persistence/kv-queues-cron/) — the durable-by-default KV
model and how it backs queues and cron.
diff --git a/docs/site/reference/mcp/index.md b/docs/site/reference/mcp/index.md
index 68e3d9f03..75af9c8a7 100644
--- a/docs/site/reference/mcp/index.md
+++ b/docs/site/reference/mcp/index.md
@@ -17,7 +17,7 @@ documentation, and trigger allowlisted CLI commands — over newline-delimited J
no npm MCP SDK on the dependency graph.
Most consumers never import this package: `netscript agent init` installs it as an MCP server and
-`netscript agent mcp` runs it. See [Agent tooling](/capabilities/agent-tooling/) for the CLI ×
+`netscript agent mcp` runs it. See [Agent tooling](/ai/agent-tooling/) for the CLI ×
skills × MCP combo, and the package README for the mental model and recipes.
Two entrypoints carry the surface:
diff --git a/docs/site/reference/queue/index.md b/docs/site/reference/queue/index.md
index 539f89388..7849137ca 100644
--- a/docs/site/reference/queue/index.md
+++ b/docs/site/reference/queue/index.md
@@ -125,9 +125,9 @@ documented in the sections above.
## See it live
-- **How-to:** [Queue / KV / cron](/how-to/queue-kv-cron/) — enqueue, consume, and schedule against
+- **How-to:** [Queue / KV / cron](/data-persistence/how-to/queue-kv-cron/) — enqueue, consume, and schedule against
these symbols end to end.
-- **How-to:** [Choose a queue provider](/how-to/choose-a-queue-provider/) — pick and pin a backend
+- **How-to:** [Choose a queue provider](/data-persistence/how-to/choose-a-queue-provider/) — pick and pin a backend
from the `QueueProvider` enum above.
- **Concept:** [KV, queues & cron](/data-persistence/kv-queues-cron/) — why a queue is
provider-agnostic and how Aspire wires the four backends.
diff --git a/docs/site/how-to/add-a-service.md b/docs/site/services-sdk/how-to/add-a-service.md
similarity index 98%
rename from docs/site/how-to/add-a-service.md
rename to docs/site/services-sdk/how-to/add-a-service.md
index fab56484e..1794ea07e 100644
--- a/docs/site/how-to/add-a-service.md
+++ b/docs/site/services-sdk/how-to/add-a-service.md
@@ -2,8 +2,8 @@
layout: layouts/base.vto
title: Add a service
templateEngine: [vento, md]
-prev: { label: "Add a plugin", href: "/how-to/add-a-plugin/" }
-next: { label: "Database & migration", href: "/how-to/database-migration/" }
+order: 101
+oldUrl: /how-to/add-a-service/
---
# Add a service
@@ -320,7 +320,7 @@ Wire persistence with the database recipe before you depend on durability.
{ title: "Tutorial: Build a service", body: "The guided, learning-oriented version — contract to typed client to a Fresh island, explained step by step.", href: "/tutorials/storefront/02-catalog-service/", icon: "→" },
{ title: "Service API reference", body: "The full generated surface of defineService and createService — every option, builder method, and return type.", href: "/reference/service/", icon: "◆" },
{ title: "Contracts, explained", body: "How an oRPC contract flows from service to typed client to UI without a codegen step.", href: "/explanation/contracts/", icon: "◎" },
- { title: "Database & migration", body: "Replace the seeded in-memory records with real Prisma-backed persistence — Postgres is the recommended engine, or mysql / mssql / sqlite via --db — init, generate, seed (Aspire up first).", href: "/how-to/database-migration/", icon: "▣" }
+ { title: "Database & migration", body: "Replace the seeded in-memory records with real Prisma-backed persistence — Postgres is the recommended engine, or mysql / mssql / sqlite via --db — init, generate, seed (Aspire up first).", href: "/data-persistence/how-to/database-migration/", icon: "▣" }
] }) }}
Manage the service over its lifetime by editing its contract under `contracts/versions/`
diff --git a/docs/site/how-to/discover-services.md b/docs/site/services-sdk/how-to/discover-services.md
similarity index 98%
rename from docs/site/how-to/discover-services.md
rename to docs/site/services-sdk/how-to/discover-services.md
index 7eebe4581..21a309d3e 100644
--- a/docs/site/how-to/discover-services.md
+++ b/docs/site/services-sdk/how-to/discover-services.md
@@ -2,8 +2,8 @@
layout: layouts/base.vto
title: Discover services
templateEngine: [vento, md]
-prev: { label: "How-to guides", href: "/how-to/" }
-next: { label: "Expose OpenAPI & Scalar", href: "/how-to/expose-openapi-scalar/" }
+order: 102
+oldUrl: /how-to/discover-services/
---
# Discover services
@@ -275,7 +275,7 @@ var) or the client cannot find the service.
## See also
{{ comp.featureGrid({ items: [
- { title: "Do — Add a service", body: "Create the callee first: contract → defineService → an oRPC service answering on its own port.", href: "/how-to/add-a-service/", icon: "◆" },
+ { title: "Do — Add a service", body: "Create the callee first: contract → defineService → an oRPC service answering on its own port.", href: "/services-sdk/how-to/add-a-service/", icon: "◆" },
{ title: "Capability — Services & contracts", body: "Why the shared contract object is the single source of truth, and how a typed client cannot drift from the service.", href: "/capabilities/services/", icon: "▣" },
{ title: "Look up — @netscript/sdk", body: "The full SDK surface: createServiceClient, defineServices, the discovery readers, query factories, and TanStack utils.", href: "/reference/sdk/", icon: "≡" },
{ title: "Understand — Orchestration with Aspire", body: "How Aspire wires resources, resolves endpoints, and injects the services__ env vars discovery reads.", href: "/explanation/aspire/", icon: "◎" }
@@ -285,4 +285,4 @@ For the concepts behind the typed-client surface see {{ comp.xref({ key: "cap:sd
the sibling recipe that builds the callee is {{ comp.xref({ key: "howto:add-a-service" }) }};
and the orchestration that injects the URLs is explained in {{ comp.xref({ key: "explain:aspire" }) }}.
-{{ comp.nextPrev({ prev: { label: "How-to guides", href: "/how-to/" }, next: { label: "Expose OpenAPI & Scalar", href: "/how-to/expose-openapi-scalar/" } }) }}
+{{ comp.nextPrev({ prev: { label: "How-to guides", href: "/how-to/" }, next: { label: "Expose OpenAPI & Scalar", href: "/services-sdk/how-to/expose-openapi-scalar/" } }) }}
diff --git a/docs/site/how-to/expose-openapi-scalar.md b/docs/site/services-sdk/how-to/expose-openapi-scalar.md
similarity index 95%
rename from docs/site/how-to/expose-openapi-scalar.md
rename to docs/site/services-sdk/how-to/expose-openapi-scalar.md
index a4c2b2db0..b9f2d90fc 100644
--- a/docs/site/how-to/expose-openapi-scalar.md
+++ b/docs/site/services-sdk/how-to/expose-openapi-scalar.md
@@ -2,8 +2,8 @@
layout: layouts/base.vto
title: Expose OpenAPI & Scalar
templateEngine: [vento, md]
-prev: { label: "Discover services", href: "/how-to/discover-services/" }
-next: { label: "Use a second database", href: "/how-to/use-a-second-database/" }
+order: 103
+oldUrl: /how-to/expose-openapi-scalar/
---
# Expose OpenAPI & Scalar
@@ -181,13 +181,13 @@ no zod input/output yields an empty operation schema in the spec. Add schemas (a
{
title: "Do — Discover services",
body: "Recipe: enumerate the services in a workspace and the endpoints each one exposes.",
- href: "/how-to/discover-services/",
+ href: "/services-sdk/how-to/discover-services/",
icon: "◆"
},
{
title: "Do — Graceful shutdown",
body: "Recipe: drain in-flight requests and run teardown hooks before the service process exits.",
- href: "/how-to/graceful-shutdown/",
+ href: "/orchestration-runtime/how-to/graceful-shutdown/",
icon: "◆"
},
{
@@ -198,4 +198,4 @@ no zod input/output yields an empty operation schema in the spec. Add schemas (a
}
] }) }}
-{{ comp.nextPrev({ prev: { label: "Discover services", href: "/how-to/discover-services/" }, next: { label: "Use a second database", href: "/how-to/use-a-second-database/" } }) }}
+{{ comp.nextPrev({ prev: { label: "Discover services", href: "/services-sdk/how-to/discover-services/" }, next: { label: "Use a second database", href: "/data-persistence/how-to/use-a-second-database/" } }) }}
diff --git a/docs/site/services-sdk/how-to/index.md b/docs/site/services-sdk/how-to/index.md
new file mode 100644
index 000000000..fc5f22f4f
--- /dev/null
+++ b/docs/site/services-sdk/how-to/index.md
@@ -0,0 +1,12 @@
+---
+layout: layouts/base.vto
+title: Recipes
+templateEngine: [vento, md]
+order: 100
+---
+
+# Services & SDK — recipes
+
+Task-oriented recipes for this area. Each one solves a single concrete problem and assumes you know
+the basics from the guides above it in the sidebar. The full cross-area catalog lives at
+[All how-to recipes]({{ "howto:index" |> xref |> url }}).
diff --git a/docs/site/services-sdk/index.md b/docs/site/services-sdk/index.md
index d35c0d1e0..08c51e1b9 100644
--- a/docs/site/services-sdk/index.md
+++ b/docs/site/services-sdk/index.md
@@ -37,8 +37,17 @@ via ALPN automatically. See
{{ comp.cardsGrid({ columns: 3, cards: [
{ eyebrow: "Overview & Concepts", title: "Contracts to service to client", body: "The shared model for service handlers, OpenAPI, RPC, and typed clients.", href: "/explanation/contracts/", icon: "O" },
{ eyebrow: "Quickstart", title: "Catalog service", body: "Create the first service in the Storefront tutorial.", href: "/tutorials/storefront/02-catalog-service/", icon: "Q" },
- { eyebrow: "How-To", title: "Add a service", body: "Add a new service to a workspace.", href: "/how-to/add-a-service/", icon: "H" },
- { eyebrow: "How-To", title: "Discover services", body: "Resolve service URLs and clients from the generated workspace.", href: "/how-to/discover-services/", icon: "H" },
- { eyebrow: "How-To", title: "OpenAPI and Scalar", body: "Expose the generated OpenAPI document and Scalar UI.", href: "/how-to/expose-openapi-scalar/", icon: "H" },
+ { eyebrow: "How-To", title: "Add a service", body: "Add a new service to a workspace.", href: "/services-sdk/how-to/add-a-service/", icon: "H" },
+ { eyebrow: "How-To", title: "Discover services", body: "Resolve service URLs and clients from the generated workspace.", href: "/services-sdk/how-to/discover-services/", icon: "H" },
+ { eyebrow: "How-To", title: "OpenAPI and Scalar", body: "Expose the generated OpenAPI document and Scalar UI.", href: "/services-sdk/how-to/expose-openapi-scalar/", icon: "H" },
{ eyebrow: "API Reference", title: "service and sdk", body: "Generated service, SDK, and contract package symbols.", href: "/reference/service/", icon: "R" }
] }) }}
+
+## Learn, do, look up
+
+{{ comp.cardsGrid({ columns: 4, cards: [
+ { eyebrow: "Learn", title: "Storefront tutorial", body: "Define contracts and services, then consume them through the typed SDK.", href: resolveXref("tut:storefront").href },
+ { eyebrow: "Do", title: "Recipes", body: "Task-oriented recipes for this area, one problem each.", href: "/services-sdk/how-to/" },
+ { eyebrow: "Look up", title: "`@netscript/service` reference", body: "Generated API reference. Related units: `sdk`, `contracts`.", href: resolveXref("ref:service").href },
+ { eyebrow: "Understand", title: "Contracts & type flow", body: "The design rationale behind this pillar.", href: resolveXref("explain:contracts").href },
+] }) }}
diff --git a/docs/site/services-sdk/sdk.md b/docs/site/services-sdk/sdk.md
index 780ccf1db..c48f23ee1 100644
--- a/docs/site/services-sdk/sdk.md
+++ b/docs/site/services-sdk/sdk.md
@@ -4,6 +4,7 @@ title: Typed SDK & client
templateEngine: [vento, md]
prev: { label: "Fresh meta-framework", href: "/web-layer/" }
next: { label: "Polyglot tasks", href: "/background-processing/polyglot-tasks/" }
+order: 2
---
# Typed SDK & client
@@ -81,7 +82,7 @@ up on the client already typed. The mechanics of each layer are below; the type-
{
title: "Do — Discover a service",
body: "Task recipe: resolve a service URL from Aspire env and stand up a typed client for it.",
- href: "/how-to/discover-services/",
+ href: "/services-sdk/how-to/discover-services/",
icon: "◆"
}
] }) }}
diff --git a/docs/site/services-sdk/services.md b/docs/site/services-sdk/services.md
index a6d968fbb..2e9db027f 100644
--- a/docs/site/services-sdk/services.md
+++ b/docs/site/services-sdk/services.md
@@ -4,6 +4,7 @@ title: Services & contracts
templateEngine: [vento, md]
prev: { label: "Capabilities", href: "/capabilities/" }
next: { label: "Background jobs", href: "/background-processing/workers/" }
+order: 1
---
# Services & contracts
@@ -85,13 +86,13 @@ type story is in [Contracts](/explanation/contracts/).
{
title: "Do — Expose OpenAPI & Scalar",
body: "Recipe: turn on the generated OpenAPI spec and the Scalar docs UI for an existing service.",
- href: "/how-to/expose-openapi-scalar/",
+ href: "/services-sdk/how-to/expose-openapi-scalar/",
icon: "◆"
},
{
title: "Do — Graceful shutdown",
body: "Recipe: drain in-flight requests and run teardown hooks on SIGINT/SIGTERM before the process exits.",
- href: "/how-to/graceful-shutdown/",
+ href: "/orchestration-runtime/how-to/graceful-shutdown/",
icon: "◆"
}
] }) }}
diff --git a/docs/site/styles/docs.css b/docs/site/styles/docs.css
index bacedbadf..bcd237638 100644
--- a/docs/site/styles/docs.css
+++ b/docs/site/styles/docs.css
@@ -1703,3 +1703,68 @@ a.ns-cards-grid__card:focus-visible {
color: var(--ns-fg);
border-color: var(--ns-border-hover, var(--ns-accent, #5b8def));
}
+
+/* ── docs-v5 multilevel nav tree (nav plugin, _includes/nav/menu-item.vto) ── */
+
+.ns-nav-lane {
+ display: flex;
+ flex-direction: column;
+ gap: 2px;
+ margin: 0;
+}
+
+.ns-nav-lane__sub {
+ font-family: var(--ns-font-sans);
+ font-size: 0.6875rem;
+ font-weight: 400;
+ text-transform: none;
+ letter-spacing: normal;
+ color: var(--ns-muted-fg);
+ opacity: 0.8;
+}
+
+.ns-nav-branch__summary {
+ display: flex;
+ align-items: center;
+ list-style: none;
+ cursor: pointer;
+}
+
+.ns-nav-branch__summary::-webkit-details-marker {
+ display: none;
+}
+
+.ns-nav-branch__summary .ns-dashboard__nav-item {
+ flex: 1;
+ min-width: 0;
+}
+
+.ns-nav-branch__chevron {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 26px;
+ height: 26px;
+ margin-right: var(--ns-space-1-5);
+ border-radius: var(--ns-radius-sm, 4px);
+ color: var(--ns-muted-fg);
+ transition: transform 140ms ease, background-color 140ms ease;
+}
+
+.ns-nav-branch__chevron:hover {
+ background: color-mix(in srgb, var(--ns-surface-raised) 60%, transparent);
+ color: var(--ns-fg);
+}
+
+.ns-nav-branch[open] > .ns-nav-branch__summary .ns-nav-branch__chevron {
+ transform: rotate(90deg);
+}
+
+.ns-nav-branch__children {
+ display: flex;
+ flex-direction: column;
+ gap: 2px;
+ margin-left: var(--ns-space-3);
+ padding-left: var(--ns-space-2);
+ border-left: 1px solid var(--ns-border);
+}
diff --git a/docs/site/tutorials/chat/03-chat-ui.md b/docs/site/tutorials/chat/03-chat-ui.md
index 83a0402fb..94a75c1cc 100644
--- a/docs/site/tutorials/chat/03-chat-ui.md
+++ b/docs/site/tutorials/chat/03-chat-ui.md
@@ -37,7 +37,7 @@ netscript ui:add ai
This copies component files into `apps/dashboard/components/ui/`, the `chat-render` parser
into `apps/dashboard/lib/chat/parse-blocks.ts`, and their CSS into `apps/dashboard/assets/ui/`,
then wires the styles and merges any required imports. After the copy, that code is yours to
-edit — see [Customize Fresh UI](/how-to/customize-fresh-ui/) for the ownership model.
+edit — see [Customize Fresh UI](/web-layer/how-to/customize-fresh-ui/) for the ownership model.
{{ comp.apiTable({
caption: "The ai collection — the pieces this track uses",
@@ -67,7 +67,7 @@ const seed = await resolveChatSnapshot({ target: { sessionId } });
```
Wire `seed` into your page with the scaffold's page builder the same way the dashboard
-passes data to a view — see [Customize Fresh UI](/how-to/customize-fresh-ui/) for the
+passes data to a view — see [Customize Fresh UI](/web-layer/how-to/customize-fresh-ui/) for the
`definePage` pattern. The important part is that the island receives `seed` as a prop.
{{ comp callout { type: "note", title: "The one-projection law, in one sentence" } }}
diff --git a/docs/site/tutorials/chat/06-live-streaming.md b/docs/site/tutorials/chat/06-live-streaming.md
index 8a53a7351..b51b08fcc 100644
--- a/docs/site/tutorials/chat/06-live-streaming.md
+++ b/docs/site/tutorials/chat/06-live-streaming.md
@@ -180,11 +180,11 @@ That progression — **one agreed message shape → durable delivery → live st
NetScript real-time surface is built from; you just built it in its AI-chat form. Where to go next:
- Reach for the durable-chat seams directly with the
- [Build a durable chat](/how-to/build-a-durable-chat/) recipe.
+ [Build a durable chat](/ai/how-to/build-a-durable-chat/) recipe.
- Restyle any chat or widget component — you own the copied source:
- [Customize Fresh UI](/how-to/customize-fresh-ui/).
+ [Customize Fresh UI](/web-layer/how-to/customize-fresh-ui/).
- Model live list/board/table data (the other real-time plane) with
- [Publish a durable stream](/how-to/publish-a-durable-stream/).
+ [Publish a durable stream](/durable-workflows/how-to/publish-a-durable-stream/).
- Compose the provider-neutral engine — model registry, agent loop, tool registry, MCP
transports: [AI engine](/ai/engine/).
- Look up exact signatures in the [fresh reference](/reference/fresh/).
diff --git a/docs/site/tutorials/chat/index.md b/docs/site/tutorials/chat/index.md
index 966baa9ad..2c14be20f 100644
--- a/docs/site/tutorials/chat/index.md
+++ b/docs/site/tutorials/chat/index.md
@@ -4,6 +4,7 @@ title: AI Chat
templateEngine: [vento, md]
prev: { label: "Tutorials", href: "/tutorials/" }
next: { label: "1 · Scaffold", href: "/tutorials/chat/01-scaffold/" }
+order: 2
---
# AI Chat
@@ -114,3 +115,5 @@ its transcript, how a tool call (yours or a remote MCP server's) lands as a dura
and how MCP `ui://` widgets render themed and sandboxed inside the chat.
{{ comp.nextPrev({ prev: { label: "Tutorials", href: "/tutorials/" }, next: { label: "1 · Scaffold", href: "/tutorials/chat/01-scaffold/" } }) }}
+
+After this track, keep building in **Build › [AI & Agents](/ai/)** — the guides and recipes there pick up where these chapters stop.
diff --git a/docs/site/tutorials/eis-chat/01-scaffold.md b/docs/site/tutorials/eis-chat/01-scaffold.md
index 375824a27..d5c37602c 100644
--- a/docs/site/tutorials/eis-chat/01-scaffold.md
+++ b/docs/site/tutorials/eis-chat/01-scaffold.md
@@ -1,4 +1,5 @@
---
layout: layouts/redirect.vto
redirectTo: /tutorials/chat/01-scaffold/
+nav_hide: true
---
diff --git a/docs/site/tutorials/eis-chat/02-message-contract.md b/docs/site/tutorials/eis-chat/02-message-contract.md
index db1120645..463408997 100644
--- a/docs/site/tutorials/eis-chat/02-message-contract.md
+++ b/docs/site/tutorials/eis-chat/02-message-contract.md
@@ -1,4 +1,5 @@
---
layout: layouts/redirect.vto
redirectTo: /tutorials/chat/02-durable-chat-route/
+nav_hide: true
---
diff --git a/docs/site/tutorials/eis-chat/03-deliver-worker.md b/docs/site/tutorials/eis-chat/03-deliver-worker.md
index db1120645..463408997 100644
--- a/docs/site/tutorials/eis-chat/03-deliver-worker.md
+++ b/docs/site/tutorials/eis-chat/03-deliver-worker.md
@@ -1,4 +1,5 @@
---
layout: layouts/redirect.vto
redirectTo: /tutorials/chat/02-durable-chat-route/
+nav_hide: true
---
diff --git a/docs/site/tutorials/eis-chat/04-live-stream.md b/docs/site/tutorials/eis-chat/04-live-stream.md
index bbfb6ba19..7a9b98233 100644
--- a/docs/site/tutorials/eis-chat/04-live-stream.md
+++ b/docs/site/tutorials/eis-chat/04-live-stream.md
@@ -1,4 +1,5 @@
---
layout: layouts/redirect.vto
redirectTo: /tutorials/chat/06-live-streaming/
+nav_hide: true
---
diff --git a/docs/site/tutorials/eis-chat/index.md b/docs/site/tutorials/eis-chat/index.md
index 8068d7c65..56898979a 100644
--- a/docs/site/tutorials/eis-chat/index.md
+++ b/docs/site/tutorials/eis-chat/index.md
@@ -1,4 +1,5 @@
---
layout: layouts/redirect.vto
redirectTo: /tutorials/chat/
+nav_hide: true
---
diff --git a/docs/site/tutorials/erp-sync/01-scaffold.md b/docs/site/tutorials/erp-sync/01-scaffold.md
index 5641b2c7e..2605b4213 100644
--- a/docs/site/tutorials/erp-sync/01-scaffold.md
+++ b/docs/site/tutorials/erp-sync/01-scaffold.md
@@ -168,7 +168,7 @@ your control plane for the rest of the track.
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.
+Deploy locally with Aspire.
{{ /comp }}
## Verify your progress
diff --git a/docs/site/tutorials/erp-sync/03-polyglot-transform.md b/docs/site/tutorials/erp-sync/03-polyglot-transform.md
index 9a83b680d..d4d12dff7 100644
--- a/docs/site/tutorials/erp-sync/03-polyglot-transform.md
+++ b/docs/site/tutorials/erp-sync/03-polyglot-transform.md
@@ -147,7 +147,7 @@ Calling .build() on a deno task without
Always pass an explicit, least-privilege set. The framework also ships named
permissions presets (minimal, readOnly,
network, …) so you do not hand-roll the object for common cases — see
-Tune the worker runtime.
+Tune the worker runtime.
{{ /comp }}
## Step 3 — Run it through the executor
@@ -261,7 +261,7 @@ A missing interpreter surfaces as a failed task, not a thrown error: ex
127 is reported as command not found and 126 as command not
executable. Confirm python3 --version / pwsh --version /
dotnet --version on the actual worker host before you ship a task that depends on it.
-When yours is ready, Run a polyglot task walks the
+When yours is ready, Run a polyglot task walks the
Python and shell variants end to end, including interpreter pinning.
{{ /comp }}
@@ -312,11 +312,11 @@ on a schedule.
## Where to go deeper
-- **Run the Python/shell variants for real** → [Run a polyglot task](/how-to/run-a-polyglot-task/)
+- **Run the Python/shell variants for real** → [Run a polyglot task](/background-processing/how-to/run-a-polyglot-task/)
— the hands-on recipe: define, write the script, pin the interpreter, read the result.
- **The capability** → [Polyglot tasks](/background-processing/polyglot-tasks/) — the WHY: what a
task is, the subprocess seam, the full `TaskResult` shape.
-- **Tune the runtime** → [Tune the worker runtime](/how-to/tune-worker-runtime/) — concurrency,
+- **Tune the runtime** → [Tune the worker runtime](/background-processing/how-to/tune-worker-runtime/) — concurrency,
the permission presets, and the per-task timeout/retry knobs.
{{ comp.nextPrev({ prev: { label: "2 · Import job", href: "/tutorials/erp-sync/02-import-job/" }, next: { label: "4 · Queue & cron", href: "/tutorials/erp-sync/04-queue-and-cron/" } }) }}
diff --git a/docs/site/tutorials/erp-sync/04-queue-and-cron.md b/docs/site/tutorials/erp-sync/04-queue-and-cron.md
index 572df1ad1..95b6dee1f 100644
--- a/docs/site/tutorials/erp-sync/04-queue-and-cron.md
+++ b/docs/site/tutorials/erp-sync/04-queue-and-cron.md
@@ -67,7 +67,7 @@ Auto-discovery probes RabbitMQ → Redis → Deno KV only. It w
fall through to PostgreSQL, even when a Postgres connection is present. The SQL-durable queue is
opt-in — you must pass provider: QueueProvider.Postgres or you will quietly land on the
Deno KV adapter. The full decision guide is in
-Choose a queue provider.
+Choose a queue provider.
{{ /comp }}
## Step 2 — Size worker concurrency in config
@@ -105,7 +105,7 @@ export const workers = defineWorkers({
For per-topic control — a hot `imports` topic at concurrency 10 while a heavy `reports` topic stays
at 1 — use a `WorkerGroup` with its own `scaling: { mode, concurrency }`. The full per-topic and
-runner-mode knobs are in [Tune the worker runtime](/how-to/tune-worker-runtime/).
+runner-mode knobs are in [Tune the worker runtime](/background-processing/how-to/tune-worker-runtime/).
{{ comp callout { type: "warning", title: "Set scaling.concurrency in config — the Aspire env var is silently ignored" } }}
There are two concurrency env names in play and they are not the same
@@ -229,7 +229,7 @@ schedule, not a file event, enqueued it. Set the cron back to `0 6 * * *` when y
Raising concurrency means more jobs run at once, and every queue backend can redeliver a
message on retry. An idempotent import — keyed on the file's content hash or name — can re-run without
double-importing. Pair retries with an idempotencyKey on enqueue so a redelivery after a
-restart does not duplicate rows. See Choose a queue
+restart does not duplicate rows. See Choose a queue
provider for each backend's delivery semantics.
{{ /comp }}
diff --git a/docs/site/tutorials/erp-sync/05-deploy.md b/docs/site/tutorials/erp-sync/05-deploy.md
index 07579c833..aa99f4e1c 100644
--- a/docs/site/tutorials/erp-sync/05-deploy.md
+++ b/docs/site/tutorials/erp-sync/05-deploy.md
@@ -41,8 +41,8 @@ stitch a file drop to its job execution.
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. Shipping to a remote target (managed infrastructure, your own process lifecycle) is the
-Deploy recipe; this chapter is the local companion. The full local
-walkthrough lives in Deploy locally with Aspire.
+Deploy recipe; this chapter is the local companion. The full local
+walkthrough lives in Deploy locally with Aspire.
{{ /comp }}
## Before you begin
@@ -212,12 +212,12 @@ and you know exactly where the local story ends and a production deployment begi
You have finished the ERP Sync track. From here, branch into task-oriented and reference docs:
-- **Ship it remotely** → [Deploy](/how-to/deploy/) — the production companion to this local run:
+- **Ship it remotely** → [Deploy](/orchestration-runtime/how-to/deploy/) — the production companion to this local run:
deployable units, managed backing services, and the `--no-aspire` path.
-- **Take the transform polyglot** → [Run a polyglot task](/how-to/run-a-polyglot-task/) — swap
+- **Take the transform polyglot** → [Run a polyglot task](/background-processing/how-to/run-a-polyglot-task/) — swap
Chapter 3's Deno transform for a Python or shell step on your own host.
-- **Tune throughput** → [Choose a queue provider](/how-to/choose-a-queue-provider/) and
- [Tune the worker runtime](/how-to/tune-worker-runtime/).
+- **Tune throughput** → [Choose a queue provider](/data-persistence/how-to/choose-a-queue-provider/) and
+ [Tune the worker runtime](/background-processing/how-to/tune-worker-runtime/).
- **Understand the orchestrator** → [Orchestration with Aspire](/explanation/aspire/) and the full
[How-to guides](/how-to/).
diff --git a/docs/site/tutorials/erp-sync/index.md b/docs/site/tutorials/erp-sync/index.md
index 29b79aa81..4a5835b22 100644
--- a/docs/site/tutorials/erp-sync/index.md
+++ b/docs/site/tutorials/erp-sync/index.md
@@ -4,6 +4,7 @@ title: ERP Sync
templateEngine: [vento, md]
prev: { label: "Tutorials", href: "/tutorials/" }
next: { label: "1 · Scaffold", href: "/tutorials/erp-sync/01-scaffold/" }
+order: 5
---
# ERP Sync
@@ -122,3 +123,5 @@ instead of resetting between chapters. Follow them in order.
{{ /comp }}
{{ comp.nextPrev({ prev: { label: "Tutorials", href: "/tutorials/" }, next: { label: "1 · Scaffold", href: "/tutorials/erp-sync/01-scaffold/" } }) }}
+
+After this track, keep building in **Build › [Background jobs](/background-processing/)** — the guides and recipes there pick up where these chapters stop.
diff --git a/docs/site/tutorials/live-dashboard/02-contract-to-service.md b/docs/site/tutorials/live-dashboard/02-contract-to-service.md
index b7f95f9d0..4f45746fd 100644
--- a/docs/site/tutorials/live-dashboard/02-contract-to-service.md
+++ b/docs/site/tutorials/live-dashboard/02-contract-to-service.md
@@ -208,7 +208,7 @@ netscript db seed # seed development data (orders, users, products
These talk to the Postgres container Aspire provisioned. Run them with no Aspire up and they fail —
there is no database to reach. The full sequence is in
-[Database & migration](/how-to/database-migration/).
+[Database & migration](/data-persistence/how-to/database-migration/).
## Verify your progress
diff --git a/docs/site/tutorials/live-dashboard/03-sdk-cache-first-query.md b/docs/site/tutorials/live-dashboard/03-sdk-cache-first-query.md
index ebb4f7e2f..f381f5ea0 100644
--- a/docs/site/tutorials/live-dashboard/03-sdk-cache-first-query.md
+++ b/docs/site/tutorials/live-dashboard/03-sdk-cache-first-query.md
@@ -66,7 +66,7 @@ export const ordersClient = createServiceClient({
reading a field the output does not have, is a compile error.
{{ comp callout { type: "note", title: "How serviceName resolves to a URL" } }}
-You never hardcode http://localhost:3002. serviceName: 'orders' is resolved at call time from an Aspire-injected env var — server-side services__orders__http__0, and the browser mirror VITE_services__orders__http__0 — via getServiceUrl in @netscript/sdk/discovery. Aspire sets those when you list orders as a reference; the client just reads them. Full mechanics in Discover services.
+You never hardcode http://localhost:3002. serviceName: 'orders' is resolved at call time from an Aspire-injected env var — server-side services__orders__http__0, and the browser mirror VITE_services__orders__http__0 — via getServiceUrl in @netscript/sdk/discovery. Aspire sets those when you list orders as a reference; the client just reads them. Full mechanics in Discover services.
{{ /comp }}
## Step 2 — Add the cache-first query factory
diff --git a/docs/site/tutorials/live-dashboard/06-deploy.md b/docs/site/tutorials/live-dashboard/06-deploy.md
index 6b4854848..815552572 100644
--- a/docs/site/tutorials/live-dashboard/06-deploy.md
+++ b/docs/site/tutorials/live-dashboard/06-deploy.md
@@ -148,7 +148,7 @@ Both should return healthy responses, and the dashboard at `:18888` should list
- [ ] Creating an order is visible in the dashboard traces and the live monitor.
{{ comp callout { type: "warning", title: "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.
+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.
{{ /comp }}
{{ comp callout { type: "warning", title: "Footguns when aspire start will not boot" } }}
@@ -172,7 +172,7 @@ NetScript spine.
- **Task recipes** → the [how-to guides](/how-to/) cover what the tutorials don't: adding plugins,
database migrations, queue backends, and production pitfalls.
-- **Ship it remotely** → [Deploy](/how-to/deploy/) is the production companion to local Aspire.
+- **Ship it remotely** → [Deploy](/orchestration-runtime/how-to/deploy/) is the production companion to local Aspire.
- **Go deeper** → [Orchestration with Aspire](/explanation/aspire/) explains the AppHost, plugin
contributions, and two-pass reference resolution; {{ comp.xref({ key: "cap:streams" }) }} and
{{ comp.xref({ key: "cap:fresh-framework", text: "the Fresh meta-framework" }) }} back chapters 4
diff --git a/docs/site/tutorials/live-dashboard/index.md b/docs/site/tutorials/live-dashboard/index.md
index f819d3cb8..1732bc010 100644
--- a/docs/site/tutorials/live-dashboard/index.md
+++ b/docs/site/tutorials/live-dashboard/index.md
@@ -4,6 +4,7 @@ title: Live Dashboard
templateEngine: [vento, md]
prev: { label: "Tutorials", href: "/tutorials/" }
next: { label: "1 · Scaffold", href: "/tutorials/live-dashboard/01-scaffold/" }
+order: 1
---
# Live Dashboard
@@ -111,3 +112,5 @@ contract to durable stream. Every hop is typed off the same contract, so the row
the row the service wrote.
{{ comp.nextPrev({ prev: { label: "Tutorials", href: "/tutorials/" }, next: { label: "1 · Scaffold", href: "/tutorials/live-dashboard/01-scaffold/" } }) }}
+
+After this track, keep building in **Build › [Web Layer](/web-layer/)** — the guides and recipes there pick up where these chapters stop.
diff --git a/docs/site/tutorials/storefront/03-cart-contracts.md b/docs/site/tutorials/storefront/03-cart-contracts.md
index 14525c50e..ea16bf540 100644
--- a/docs/site/tutorials/storefront/03-cart-contracts.md
+++ b/docs/site/tutorials/storefront/03-cart-contracts.md
@@ -57,7 +57,7 @@ it.
## Step 1 — Scaffold the cart contract
Let the CLI lay down the contract file and wire it into the version aggregate for you, exactly as the
-[Add a service](/how-to/add-a-service/) recipe does. From the workspace root:
+[Add a service](/services-sdk/how-to/add-a-service/) recipe does. From the workspace root:
```sh
netscript contract add cart
diff --git a/docs/site/tutorials/storefront/06-storefront-ui.md b/docs/site/tutorials/storefront/06-storefront-ui.md
index 663eb4151..2103f136d 100644
--- a/docs/site/tutorials/storefront/06-storefront-ui.md
+++ b/docs/site/tutorials/storefront/06-storefront-ui.md
@@ -156,7 +156,7 @@ You never write http://localhost:3001. serviceName: 'products
call time from an Aspire-injected env var — server-side services__products__http__0 and
its browser mirror — so the same client works in a page loader and in a hydrated island. Aspire sets
those when the app lists products as a reference; the client just reads them. Full
-mechanics in Discover services.
+mechanics in Discover services.
{{ /comp }}
## Step 3 — Read and mutate in the island
diff --git a/docs/site/tutorials/storefront/07-deploy.md b/docs/site/tutorials/storefront/07-deploy.md
index d92b71ccb..17452a9ad 100644
--- a/docs/site/tutorials/storefront/07-deploy.md
+++ b/docs/site/tutorials/storefront/07-deploy.md
@@ -39,7 +39,7 @@ infrastructure, services, plugin APIs, and background processors — then gives
whole thing. You will read the live port map from the dashboard and confirm every resource is healthy.
{{ comp callout { type: "warning", title: "This is the LOCAL story — not a production deployer" } }}
-aspire start exists to make one command produce a complete, 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. NetScript does not ship a cloud deployer for your app; for a remote target you point processes at managed infrastructure yourself. This chapter teaches the local topology. See the Deploy and Deploy locally with Aspire how-to guides for the production-vs-local split.
+aspire start exists to make one command produce a complete, 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. NetScript does not ship a cloud deployer for your app; for a remote target you point processes at managed infrastructure yourself. This chapter teaches the local topology. See the Deploy and Deploy locally with Aspire how-to guides for the production-vs-local split.
{{ /comp }}
## Before you begin
@@ -185,9 +185,9 @@ where it counts, and verified at its edges.
## Where to go next
-- **Ship it somewhere real** → the [Deploy](/how-to/deploy/) how-to (production targets) and
- [Deploy locally with Aspire](/how-to/deploy-local-aspire/) (the full local recipe with every flag).
-- **Add observability** → [Add OpenTelemetry](/how-to/add-opentelemetry/) and the
+- **Ship it somewhere real** → the [Deploy](/orchestration-runtime/how-to/deploy/) how-to (production targets) and
+ [Deploy locally with Aspire](/orchestration-runtime/how-to/deploy-local-aspire/) (the full local recipe with every flag).
+- **Add observability** → [Add OpenTelemetry](/observability/how-to/add-opentelemetry/) and the
[Observability explanation](/explanation/observability/).
- **Go deeper on the ideas** → [Durability model](/explanation/durability-model/),
[Contracts & type flow](/explanation/contracts/), and [The plugin system](/explanation/plugin-system/).
diff --git a/docs/site/tutorials/storefront/index.md b/docs/site/tutorials/storefront/index.md
index 98895c2d9..81d1c3ff4 100644
--- a/docs/site/tutorials/storefront/index.md
+++ b/docs/site/tutorials/storefront/index.md
@@ -4,6 +4,7 @@ title: Build a storefront backend
templateEngine: [vento, md]
prev: { label: "Tutorials", href: "/tutorials/" }
next: { label: "1 · Scaffold", href: "/tutorials/storefront/01-scaffold/" }
+order: 4
---
# Build a storefront backend
@@ -121,3 +122,5 @@ This is a tutorial track: state compounds. The my-shop/ workspace y
## Start the build
{{ comp.nextPrev({ prev: { label: "Tutorials", href: "/tutorials/" }, next: { label: "1 · Scaffold", href: "/tutorials/storefront/01-scaffold/" } }) }}
+
+After this track, keep building in **Build › [Services & SDK](/services-sdk/)** — the guides and recipes there pick up where these chapters stop.
diff --git a/docs/site/tutorials/workspace/06-deploy.md b/docs/site/tutorials/workspace/06-deploy.md
index 502997dce..6d1915759 100644
--- a/docs/site/tutorials/workspace/06-deploy.md
+++ b/docs/site/tutorials/workspace/06-deploy.md
@@ -152,7 +152,7 @@ observable, correctly-wired stack on one machine. The Postgres
are throwaway Docker containers for dev convenience — not your production database or
cache, and the kv-oauth session store and auth credentials here are local-dev values. For
a remote target you point processes at managed infrastructure and let your platform own lifecycle;
-that is the Deploy recipe.
+that is the Deploy recipe.
{{ /comp }}
{{ comp callout { type: "warning", title: "Footguns when aspire start will not boot" } }}
@@ -181,7 +181,7 @@ gets a `401`; the engineer you paged gets provisioned without anyone waiting on
## Where to go next
-- **Ship it remotely** → [Deploy](/how-to/deploy/) — the production companion: deployable units,
+- **Ship it remotely** → [Deploy](/orchestration-runtime/how-to/deploy/) — the production companion: deployable units,
managed backing services, and the `--no-aspire` path.
- **Go deeper on auth** → [Authentication capability](/capabilities/auth/) and
[The authentication model](/explanation/auth-model/).
diff --git a/docs/site/tutorials/workspace/index.md b/docs/site/tutorials/workspace/index.md
index ff750576f..e7a6a2def 100644
--- a/docs/site/tutorials/workspace/index.md
+++ b/docs/site/tutorials/workspace/index.md
@@ -4,6 +4,7 @@ title: Team Workspace
templateEngine: [vento, md]
prev: { label: "Tutorials", href: "/tutorials/" }
next: { label: "1 · Scaffold", href: "/tutorials/workspace/01-scaffold/" }
+order: 3
---
# Team Workspace
@@ -134,3 +135,5 @@ chapter 1 and keep the same `my-workspace/` through to deploy.
{{ comp.nextPrev({ prev: { label: "Tutorials", href: "/tutorials/" }, next: { label: "1 · Scaffold", href: "/tutorials/workspace/01-scaffold/" } }) }}
+
+After this track, keep building in **Build › [Identity & Access](/identity-access/)** — the guides and recipes there pick up where these chapters stop.
diff --git a/docs/site/web-layer/builders.md b/docs/site/web-layer/builders.md
index 94d8519f8..b36138070 100644
--- a/docs/site/web-layer/builders.md
+++ b/docs/site/web-layer/builders.md
@@ -2,6 +2,7 @@
layout: layouts/base.vto
title: Pages and the define-page builder
templateEngine: [vento, md]
+order: 2
---
# Pages and the define-page builder
diff --git a/docs/site/web-layer/defer-streaming-ui.md b/docs/site/web-layer/defer-streaming-ui.md
index e1204c9cc..3c136b03a 100644
--- a/docs/site/web-layer/defer-streaming-ui.md
+++ b/docs/site/web-layer/defer-streaming-ui.md
@@ -2,6 +2,7 @@
layout: layouts/base.vto
title: Deferred and streaming UI
templateEngine: [vento, md]
+order: 6
---
# Deferred and streaming UI
diff --git a/docs/site/web-layer/error.md b/docs/site/web-layer/error.md
index 76ea988c1..f86c410e2 100644
--- a/docs/site/web-layer/error.md
+++ b/docs/site/web-layer/error.md
@@ -2,6 +2,7 @@
layout: layouts/base.vto
title: Error handling and diagnostics
templateEngine: [vento, md]
+order: 9
---
# Error handling and diagnostics
diff --git a/docs/site/web-layer/examples.md b/docs/site/web-layer/examples.md
index 6e3d13b00..69767b391 100644
--- a/docs/site/web-layer/examples.md
+++ b/docs/site/web-layer/examples.md
@@ -2,6 +2,7 @@
layout: layouts/base.vto
title: Examples and sandbox
templateEngine: [vento, md]
+order: 11
---
# Examples and sandbox
diff --git a/docs/site/web-layer/form.md b/docs/site/web-layer/form.md
index 523fed433..968cdbd53 100644
--- a/docs/site/web-layer/form.md
+++ b/docs/site/web-layer/form.md
@@ -2,6 +2,7 @@
layout: layouts/base.vto
title: Server-validated forms
templateEngine: [vento, md]
+order: 5
---
# Server-validated forms
diff --git a/docs/site/web-layer/fresh-ui.md b/docs/site/web-layer/fresh-ui.md
index 73f15cada..35b86d551 100644
--- a/docs/site/web-layer/fresh-ui.md
+++ b/docs/site/web-layer/fresh-ui.md
@@ -4,6 +4,7 @@ title: Fresh UI & design
templateEngine: [vento, md]
prev: { label: "Telemetry & logging", href: "/observability/telemetry/" }
next: { label: "Authentication", href: "/identity-access/auth/" }
+order: 12
---
# Fresh UI & design
@@ -47,7 +48,7 @@ drift from your oRPC contracts. Use @netscript/freshapps/dashboard
(the scaffolded app) as the worked reference to copy patterns from. For the data boundary the UI
renders see Services & contracts; to add or regenerate UI
-pieces see Customize the Fresh UI.
+pieces see Customize the Fresh UI.
{{ /comp }}
## Two layers, one design grammar
@@ -287,7 +288,7 @@ matches what you're doing.
{
title: "Do — Customize the Fresh UI",
body: "Task recipe: ui:init / ui:add, the copy-source ownership model, and editing the design tokens.",
- href: "/how-to/customize-fresh-ui/",
+ href: "/web-layer/how-to/customize-fresh-ui/",
icon: "◆"
},
{
diff --git a/docs/site/how-to/build-a-desktop-frontend.md b/docs/site/web-layer/how-to/build-a-desktop-frontend.md
similarity index 96%
rename from docs/site/how-to/build-a-desktop-frontend.md
rename to docs/site/web-layer/how-to/build-a-desktop-frontend.md
index 0115e120a..065f269db 100644
--- a/docs/site/how-to/build-a-desktop-frontend.md
+++ b/docs/site/web-layer/how-to/build-a-desktop-frontend.md
@@ -2,8 +2,8 @@
layout: layouts/base.vto
title: Building a desktop frontend the NetScript way
templateEngine: [vento, md]
-prev: { 'label': 'Customize Fresh UI', 'href': '/how-to/customize-fresh-ui/' }
-next: { 'label': 'Build a server-validated form', 'href': '/how-to/build-a-server-validated-form/' }
+order: 102
+oldUrl: /how-to/build-a-desktop-frontend/
---
# Building a desktop frontend the NetScript way
@@ -153,7 +153,7 @@ packaged-runtime check. {{ /comp }}
## See also
-- [Customize Fresh UI](/how-to/customize-fresh-ui/)
+- [Customize Fresh UI](/web-layer/how-to/customize-fresh-ui/)
- [`@netscript/fresh` reference](/reference/fresh/)
- [`@netscript/sdk` reference](/reference/sdk/)
- [`@netscript/fresh-ui` reference](/reference/fresh-ui/)
diff --git a/docs/site/how-to/build-a-server-validated-form.md b/docs/site/web-layer/how-to/build-a-server-validated-form.md
similarity index 88%
rename from docs/site/how-to/build-a-server-validated-form.md
rename to docs/site/web-layer/how-to/build-a-server-validated-form.md
index 1ca63294d..b0e32ea6f 100644
--- a/docs/site/how-to/build-a-server-validated-form.md
+++ b/docs/site/web-layer/how-to/build-a-server-validated-form.md
@@ -2,8 +2,8 @@
layout: layouts/base.vto
title: Build a server-validated form
templateEngine: [vento, md]
-prev: { label: "Add a task runtime adapter", href: "/how-to/add-a-task-runtime-adapter/" }
-next: { label: "Build a validated ingestion queue", href: "/how-to/build-a-validated-ingestion-queue/" }
+order: 103
+oldUrl: /how-to/build-a-server-validated-form/
---
# Build a server-validated form
@@ -102,6 +102,6 @@ Deno.test('contact page registers the route pattern', () => {
- Look up the builder surface in [fresh reference](/reference/fresh/).
{{ comp.nextPrev({
- prev: { label: "Add a task runtime adapter", href: "/how-to/add-a-task-runtime-adapter/" },
- next: { label: "Build a validated ingestion queue", href: "/how-to/build-a-validated-ingestion-queue/" }
+ prev: { label: "Add a task runtime adapter", href: "/background-processing/how-to/add-a-task-runtime-adapter/" },
+ next: { label: "Build a validated ingestion queue", href: "/durable-workflows/how-to/build-a-validated-ingestion-queue/" }
}) }}
diff --git a/docs/site/how-to/customize-fresh-ui.md b/docs/site/web-layer/how-to/customize-fresh-ui.md
similarity index 97%
rename from docs/site/how-to/customize-fresh-ui.md
rename to docs/site/web-layer/how-to/customize-fresh-ui.md
index 08a325adc..8be2b2a0c 100644
--- a/docs/site/how-to/customize-fresh-ui.md
+++ b/docs/site/web-layer/how-to/customize-fresh-ui.md
@@ -2,8 +2,8 @@
layout: layouts/base.vto
title: Customize Fresh UI
templateEngine: [vento, md]
-prev: { "label": "Add OpenTelemetry", "href": "/how-to/add-opentelemetry/" }
-next: { "label": "Build a durable chat", "href": "/how-to/build-a-durable-chat/" }
+order: 101
+oldUrl: /how-to/customize-fresh-ui/
---
# Customize Fresh UI
@@ -204,7 +204,7 @@ When an island or route needs server data, prefer the runtime's typed query laye
(@netscript/fresh/query) over hand-rolled fetch calls — it
carries the oRPC contract types through to the client so a renamed service field
surfaces as a type error. See @netscript/fresh
-for the query builders, and Add a service for
+for the query builders, and Add a service for
the backend half of that contract.
{{ /comp }}
@@ -389,9 +389,9 @@ type.
[`@netscript/fresh`](/reference/fresh/). These are the authority for every export
(the `/server`, `/query`, and sibling subpaths included); this guide never
duplicates them.
-- Related recipes: [Add a service](/how-to/add-a-service/) to give your UI a typed
- oRPC backend, and [Add OpenTelemetry](/how-to/add-opentelemetry/) to trace it.
+- Related recipes: [Add a service](/services-sdk/how-to/add-a-service/) to give your UI a typed
+ oRPC backend, and [Add OpenTelemetry](/observability/how-to/add-opentelemetry/) to trace it.
- Concepts: the [contracts](/explanation/contracts/) explanation shows how a typed
contract flows from service to client to island.
-{{ comp.nextPrev({ prev: { label: "Add OpenTelemetry", href: "/how-to/add-opentelemetry/" }, next: { label: "Build a durable chat", href: "/how-to/build-a-durable-chat/" } }) }}
+{{ comp.nextPrev({ prev: { label: "Add OpenTelemetry", href: "/observability/how-to/add-opentelemetry/" }, next: { label: "Build a durable chat", href: "/ai/how-to/build-a-durable-chat/" } }) }}
diff --git a/docs/site/web-layer/how-to/index.md b/docs/site/web-layer/how-to/index.md
new file mode 100644
index 000000000..67c4b251c
--- /dev/null
+++ b/docs/site/web-layer/how-to/index.md
@@ -0,0 +1,12 @@
+---
+layout: layouts/base.vto
+title: Recipes
+templateEngine: [vento, md]
+order: 100
+---
+
+# Web Layer — recipes
+
+Task-oriented recipes for this area. Each one solves a single concrete problem and assumes you know
+the basics from the guides above it in the sidebar. The full cross-area catalog lives at
+[All how-to recipes]({{ "howto:index" |> xref |> url }}).
diff --git a/docs/site/web-layer/index.md b/docs/site/web-layer/index.md
index a7a1b03af..51414ad50 100644
--- a/docs/site/web-layer/index.md
+++ b/docs/site/web-layer/index.md
@@ -72,8 +72,17 @@ scaffolded dashboard app — is [Fresh UI & design](/web-layer/fresh-ui/), which
{ eyebrow: "Overview & Concepts", title: "Fresh page model", body: "Server rendering, islands, route contracts, layers, partials, and shared query cache.", href: "/web-layer/server/", icon: "O" },
{ eyebrow: "Overview & Concepts", title: "Fresh UI & design", body: "The copy-source component registry, design tokens, and the scaffolded dashboard app.", href: "/web-layer/fresh-ui/", icon: "O" },
{ eyebrow: "Quickstart", title: "Live dashboard", body: "Build a Fresh page backed by a typed SDK client and a cache-first QueryIsland.", href: "/tutorials/live-dashboard/", icon: "Q" },
- { eyebrow: "How-To", title: "Customize Fresh UI", body: "Adjust the generated UI layer and design-system surface.", href: "/how-to/customize-fresh-ui/", icon: "H" },
- { eyebrow: "How-To", title: "Server-validated form", body: "Build a form that validates and mutates on the server.", href: "/how-to/build-a-server-validated-form/", icon: "H" },
+ { eyebrow: "How-To", title: "Customize Fresh UI", body: "Adjust the generated UI layer and design-system surface.", href: "/web-layer/how-to/customize-fresh-ui/", icon: "H" },
+ { eyebrow: "How-To", title: "Server-validated form", body: "Build a form that validates and mutates on the server.", href: "/web-layer/how-to/build-a-server-validated-form/", icon: "H" },
{ eyebrow: "API Reference", title: "@netscript/fresh", body: "Generated symbols for the Fresh framework package.", href: "/reference/fresh/", icon: "R" },
{ eyebrow: "API Reference", title: "@netscript/fresh-ui", body: "Generated symbols for the companion UI package.", href: "/reference/fresh-ui/", icon: "R" }
] }) }}
+
+## Learn, do, look up
+
+{{ comp.cardsGrid({ columns: 4, cards: [
+ { eyebrow: "Learn", title: "Live dashboard tutorial", body: "Contract to page to live stream — the web layer end to end.", href: resolveXref("tut:live-dashboard").href },
+ { eyebrow: "Do", title: "Recipes", body: "Task-oriented recipes for this area, one problem each.", href: "/web-layer/how-to/" },
+ { eyebrow: "Look up", title: "`@netscript/fresh` reference", body: "Generated API reference. Related units: `fresh-ui`.", href: resolveXref("ref:fresh").href },
+ { eyebrow: "Understand", title: "Contracts & type flow", body: "The design rationale behind this pillar.", href: resolveXref("explain:contracts").href },
+] }) }}
diff --git a/docs/site/web-layer/interactive.md b/docs/site/web-layer/interactive.md
index f1c484588..0039c9032 100644
--- a/docs/site/web-layer/interactive.md
+++ b/docs/site/web-layer/interactive.md
@@ -2,6 +2,7 @@
layout: layouts/base.vto
title: Interactive islands
templateEngine: [vento, md]
+order: 7
---
# Interactive islands
diff --git a/docs/site/web-layer/query.md b/docs/site/web-layer/query.md
index dfbbf3436..eee644158 100644
--- a/docs/site/web-layer/query.md
+++ b/docs/site/web-layer/query.md
@@ -2,6 +2,7 @@
layout: layouts/base.vto
title: Data loading and the query cache
templateEngine: [vento, md]
+order: 4
---
# Data loading and the query cache
diff --git a/docs/site/web-layer/route.md b/docs/site/web-layer/route.md
index 74ced47d9..18c43b6f4 100644
--- a/docs/site/web-layer/route.md
+++ b/docs/site/web-layer/route.md
@@ -2,6 +2,7 @@
layout: layouts/base.vto
title: Routing and route contracts
templateEngine: [vento, md]
+order: 3
---
# Routing and route contracts
diff --git a/docs/site/web-layer/server.md b/docs/site/web-layer/server.md
index 9e74a359f..6d7112e63 100644
--- a/docs/site/web-layer/server.md
+++ b/docs/site/web-layer/server.md
@@ -2,6 +2,7 @@
layout: layouts/base.vto
title: The Fresh page model
templateEngine: [vento, md]
+order: 1
---
# The Fresh page model
diff --git a/docs/site/web-layer/testing.md b/docs/site/web-layer/testing.md
index 6142543fe..21e70a797 100644
--- a/docs/site/web-layer/testing.md
+++ b/docs/site/web-layer/testing.md
@@ -2,6 +2,7 @@
layout: layouts/base.vto
title: Testing Fresh pages
templateEngine: [vento, md]
+order: 10
---
# Testing Fresh pages
diff --git a/docs/site/web-layer/vite.md b/docs/site/web-layer/vite.md
index 2e35bb6ce..21bf97e3e 100644
--- a/docs/site/web-layer/vite.md
+++ b/docs/site/web-layer/vite.md
@@ -2,6 +2,7 @@
layout: layouts/base.vto
title: Build and Vite integration
templateEngine: [vento, md]
+order: 8
---
# Build and Vite integration
`, zero JS. A reader sees ~5 lane headers + 9 pillar summaries + one open +subtree — strictly fewer on-screen elements than today's 120. + +**Breadcrumb (`breadcrumb.vto`).** Replace the `navSections` scan with `nav.breadcrumb(url)` +(root→page array), prefixed by the owning lane label via a small `root→lane` lookup (so +`/web-layer/query/` reads *Build › Web Layer › Data loading & cache*). Guard `{{ if crumb.data }}`. +Behavior change accepted: one deterministic trail replaces "last matching section." + +**Next/prev (`nextPrev.vto`).** Keep front-matter-driven for tutorials (per-chapter editorial +control; it never touched `navSections`, cannot break). For Reference (32 units, unmaintainable by +hand) auto-derive with the verified v2.5.4 signature — `nav.nextPage(url, query?, sort?)` / +`nav.previousPage(url, query?, sort?)` (there is **no** basePath argument; nav.ts:85/111). Scope to +the reference lane via the query: `nav.nextPage(url, "url^=/reference/", "order url")`. + +**Untouched chrome (verified nav-agnostic):** `
database/mysql/schema/) with its own generate output and its own
migrations — it does not merge into the Postgres aggregation above. Generate and
migrate each datasource independently. The full step-by-step lives in the
-Use a second database recipe.
+Use a second database recipe.
{{ /comp }}
## Endpoints & ports
@@ -389,13 +390,13 @@ lane that matches what you're doing.
{
title: "Do — Use a second database",
body: "Task recipe: add an MSSQL or MySQL datasource beside Postgres with a driver adapter.",
- href: "/how-to/use-a-second-database/",
+ href: "/data-persistence/how-to/use-a-second-database/",
icon: "◆"
},
{
title: "Do — Database & migration",
body: "Task recipe: bring up Postgres with Aspire, then run db init → generate → seed → status → migrate.",
- href: "/how-to/database-migration/",
+ href: "/data-persistence/how-to/database-migration/",
icon: "◆"
},
{
diff --git a/docs/site/how-to/choose-a-queue-provider.md b/docs/site/data-persistence/how-to/choose-a-queue-provider.md
similarity index 97%
rename from docs/site/how-to/choose-a-queue-provider.md
rename to docs/site/data-persistence/how-to/choose-a-queue-provider.md
index abafebc0c..f0ad9911c 100644
--- a/docs/site/how-to/choose-a-queue-provider.md
+++ b/docs/site/data-persistence/how-to/choose-a-queue-provider.md
@@ -2,8 +2,8 @@
layout: layouts/base.vto
title: Choose a queue provider
templateEngine: [vento, md]
-prev: { label: "Use a second database", href: "/how-to/use-a-second-database/" }
-next: { label: "Tune the worker runtime", href: "/how-to/tune-worker-runtime/" }
+order: 104
+oldUrl: /how-to/choose-a-queue-provider/
---
# Choose a queue provider
@@ -12,7 +12,7 @@ next: { label: "Tune the worker runtime", href: "/how-to/tune-worker-runtime/" }
adapter, the zero-config Deno KV default, Redis, RabbitMQ (AMQP), and PostgreSQL — and how
to either let auto-discovery select one or pin one explicitly with `provider` +
`connection`. This is the *decision* recipe; for the enqueue/consume/cron mechanics see
-[Queue / KV / cron](/how-to/queue-kv-cron/).
+[Queue / KV / cron](/data-persistence/how-to/queue-kv-cron/).
A NetScript queue is provider-agnostic by design: the same `createQueue("jobs")` runs on an
in-memory adapter in a unit test, on Deno KV on your laptop, and on a real broker under
@@ -177,7 +177,7 @@ await queue.listen(async (message) => {
```
For CPU-bound work, prefer Web Workers over queue concurrency — see
-[Tune the worker runtime](/how-to/tune-worker-runtime/).
+[Tune the worker runtime](/background-processing/how-to/tune-worker-runtime/).
## Prove it end to end — same call site, two backends
@@ -256,4 +256,4 @@ For the full `QueueProvider` enum, the `QueueConnectionOptions` shape, `createPa
/ `ParallelQueueOptions`, the `MessageQueue` interface, and the `QueueError` hierarchy, see
{{ comp.xref({ key: "ref:queue" }) }}.
-{{ comp.nextPrev({ prev: { label: "Use a second database", href: "/how-to/use-a-second-database/" }, next: { label: "Tune the worker runtime", href: "/how-to/tune-worker-runtime/" } }) }}
+{{ comp.nextPrev({ prev: { label: "Use a second database", href: "/data-persistence/how-to/use-a-second-database/" }, next: { label: "Tune the worker runtime", href: "/background-processing/how-to/tune-worker-runtime/" } }) }}
diff --git a/docs/site/how-to/database-migration.md b/docs/site/data-persistence/how-to/database-migration.md
similarity index 97%
rename from docs/site/how-to/database-migration.md
rename to docs/site/data-persistence/how-to/database-migration.md
index bcc1b7633..2942cf7da 100644
--- a/docs/site/how-to/database-migration.md
+++ b/docs/site/data-persistence/how-to/database-migration.md
@@ -2,8 +2,8 @@
layout: layouts/base.vto
title: Database & migration
templateEngine: [vento, md]
-prev: { label: "Add a service", href: "/how-to/add-a-service/" }
-next: { label: "Queue / KV / cron", href: "/how-to/queue-kv-cron/" }
+order: 101
+oldUrl: /how-to/database-migration/
---
# Database & migration
@@ -64,7 +64,7 @@ Aspire is keeping alive in a second terminal.
] }) }}
If you scaffolded with `--no-aspire`, you are responsible for pointing the workspace at
-your own Postgres via `POSTGRES_URI` / `DATABASE_URL` (see [Deploy](/how-to/deploy/));
+your own Postgres via `POSTGRES_URI` / `DATABASE_URL` (see [Deploy](/orchestration-runtime/how-to/deploy/));
there is no AppHost to start, so you skip Step 2, but the migration commands in Step 3
are otherwise identical and talk to whatever database those variables resolve to.
@@ -238,7 +238,7 @@ import the generated client in a service or worker and read the rows the seed wr
Once the schema is live, the generated client is what every other capability reads and
writes through. Queue jobs persist their `message_queue` rows (the
-[PostgreSQL queue backend](/how-to/queue-kv-cron/) shares this same datasource), durable
+[PostgreSQL queue backend](/data-persistence/how-to/queue-kv-cron/) shares this same datasource), durable
sagas persist runtime state when configured with the Prisma store backend, and your
services query their models directly. The migration loop you just ran is the foundation the
rest of the workspace stands on.
@@ -251,8 +251,8 @@ rest of the workspace stands on.
{{ comp.card({ title: "Orchestration with Aspire", body: "Why the AppHost (aspire/apphost.mts) provisions Postgres and Redis, and how the resource graph fits together.", href: "/explanation/aspire/", icon: "◆" }) }}
-{{ comp.card({ title: "Queue / KV / cron", body: "The next recipe: use the KV and queue backends — including the PostgreSQL queue provider that shares this datasource.", href: "/how-to/queue-kv-cron/", icon: "→" }) }}
+{{ comp.card({ title: "Queue / KV / cron", body: "The next recipe: use the KV and queue backends — including the PostgreSQL queue provider that shares this datasource.", href: "/data-persistence/how-to/queue-kv-cron/", icon: "→" }) }}
-{{ comp.card({ title: "Deploy without Aspire", body: "Point the workspace at your own Postgres via POSTGRES_URI / DATABASE_URL using the --no-aspire escape hatch.", href: "/how-to/deploy/", icon: "→" }) }}
+{{ comp.card({ title: "Deploy without Aspire", body: "Point the workspace at your own Postgres via POSTGRES_URI / DATABASE_URL using the --no-aspire escape hatch.", href: "/orchestration-runtime/how-to/deploy/", icon: "→" }) }}
For the MySQL adapter surface, see [`prisma-adapter-mysql`](/reference/prisma-adapter-mysql/).
diff --git a/docs/site/data-persistence/how-to/index.md b/docs/site/data-persistence/how-to/index.md
new file mode 100644
index 000000000..858a0ab27
--- /dev/null
+++ b/docs/site/data-persistence/how-to/index.md
@@ -0,0 +1,12 @@
+---
+layout: layouts/base.vto
+title: Recipes
+templateEngine: [vento, md]
+order: 100
+---
+
+# Data & Persistence — recipes
+
+Task-oriented recipes for this area. Each one solves a single concrete problem and assumes you know
+the basics from the guides above it in the sidebar. The full cross-area catalog lives at
+[All how-to recipes]({{ "howto:index" |> xref |> url }}).
diff --git a/docs/site/how-to/queue-kv-cron.md b/docs/site/data-persistence/how-to/queue-kv-cron.md
similarity index 98%
rename from docs/site/how-to/queue-kv-cron.md
rename to docs/site/data-persistence/how-to/queue-kv-cron.md
index 68d2fa57e..8bcad4d78 100644
--- a/docs/site/how-to/queue-kv-cron.md
+++ b/docs/site/data-persistence/how-to/queue-kv-cron.md
@@ -2,8 +2,8 @@
layout: layouts/base.vto
title: Queue / KV / cron
templateEngine: [vento, md]
-prev: { label: "Database & migration", href: "/how-to/database-migration/" }
-next: { label: "Add OpenTelemetry", href: "/how-to/add-opentelemetry/" }
+order: 103
+oldUrl: /how-to/queue-kv-cron/
---
# Queue / KV / cron
@@ -350,9 +350,9 @@ adapter classes are documented in [`@netscript/cron`](/reference/cron/).
{
title: "Database & migration",
body: "The previous recipe — provision Postgres under Aspire and run the db init → generate → seed flow.",
- href: "/how-to/database-migration/",
+ href: "/data-persistence/how-to/database-migration/",
icon: "→"
}
] }) }}
-{{ comp.nextPrev({ prev: { label: "Database & migration", href: "/how-to/database-migration/" }, next: { label: "Add OpenTelemetry", href: "/how-to/add-opentelemetry/" } }) }}
+{{ comp.nextPrev({ prev: { label: "Database & migration", href: "/data-persistence/how-to/database-migration/" }, next: { label: "Add OpenTelemetry", href: "/observability/how-to/add-opentelemetry/" } }) }}
diff --git a/docs/site/how-to/use-a-second-database.md b/docs/site/data-persistence/how-to/use-a-second-database.md
similarity index 98%
rename from docs/site/how-to/use-a-second-database.md
rename to docs/site/data-persistence/how-to/use-a-second-database.md
index de510b489..121f08bab 100644
--- a/docs/site/how-to/use-a-second-database.md
+++ b/docs/site/data-persistence/how-to/use-a-second-database.md
@@ -2,8 +2,8 @@
layout: layouts/base.vto
title: Use a second database
templateEngine: [vento, md]
-prev: { label: "Expose OpenAPI & Scalar", href: "/how-to/expose-openapi-scalar/" }
-next: { label: "Choose a queue provider", href: "/how-to/choose-a-queue-provider/" }
+order: 102
+oldUrl: /how-to/use-a-second-database/
---
# Use a second database
@@ -19,7 +19,7 @@ file-backed SQLite primary instead. Everything below applies regardless of which
primary uses; the examples simply show the common Postgres-primary case.
NetScript's default scaffold gives you **one primary datasource** that every plugin
-aggregates its `.prisma` models into (see [Database & migration](/how-to/database-migration/)).
+aggregates its `.prisma` models into (see [Database & migration](/data-persistence/how-to/database-migration/)).
A second database is the opposite shape: a **separate** Prisma schema workspace with its own
`generate` output and its own migration history. It never merges into the primary aggregation.
@@ -265,4 +265,4 @@ The order is always: build the adapter, call getDriverAdapter(), pa
{{ comp.xref({ key: "explain:aspire", text: "Orchestration with Aspire" }) }} ·
{{ comp.xref({ key: "cap:kv-queues-cron", text: "KV, queues & cron — the Postgres queue backend" }) }}
-{{ comp.nextPrev({ prev: { label: "Expose OpenAPI & Scalar", href: "/how-to/expose-openapi-scalar/" }, next: { label: "Choose a queue provider", href: "/how-to/choose-a-queue-provider/" } }) }}
+{{ comp.nextPrev({ prev: { label: "Expose OpenAPI & Scalar", href: "/services-sdk/how-to/expose-openapi-scalar/" }, next: { label: "Choose a queue provider", href: "/data-persistence/how-to/choose-a-queue-provider/" } }) }}
diff --git a/docs/site/data-persistence/index.md b/docs/site/data-persistence/index.md
index cc7f99066..52abf1d5c 100644
--- a/docs/site/data-persistence/index.md
+++ b/docs/site/data-persistence/index.md
@@ -28,8 +28,17 @@ workspace resolves a second database.
{{ comp.cardsGrid({ columns: 3, cards: [
{ eyebrow: "Overview & Concepts", title: "Database and KV", body: "The persistence layer behind services, queues, and workflow state.", href: "/data-persistence/database/", icon: "O" },
{ eyebrow: "Quickstart", title: "Data contracts", body: "Follow the Storefront data step before service and saga work expand it.", href: "/tutorials/storefront/03-cart-contracts/", icon: "Q" },
- { eyebrow: "How-To", title: "Database and migration", body: "Initialize and migrate the primary datasource.", href: "/how-to/database-migration/", icon: "H" },
- { eyebrow: "How-To", title: "Second database", body: "Add and address another datasource from the workspace.", href: "/how-to/use-a-second-database/", icon: "H" },
+ { eyebrow: "How-To", title: "Database and migration", body: "Initialize and migrate the primary datasource.", href: "/data-persistence/how-to/database-migration/", icon: "H" },
+ { eyebrow: "How-To", title: "Second database", body: "Add and address another datasource from the workspace.", href: "/data-persistence/how-to/use-a-second-database/", icon: "H" },
{ eyebrow: "API Reference", title: "database", body: "Generated database package symbols.", href: "/reference/database/", icon: "R" },
{ eyebrow: "API Reference", title: "kv and Prisma adapter", body: "Generated KV and adapter package symbols.", href: "/reference/kv/", icon: "R" }
] }) }}
+
+## Learn, do, look up
+
+{{ comp.cardsGrid({ columns: 4, cards: [
+ { eyebrow: "Learn", title: "Storefront tutorial", body: "Cart contracts back a real database schema from chapter 3 on.", href: resolveXref("tut:storefront").href },
+ { eyebrow: "Do", title: "Recipes", body: "Task-oriented recipes for this area, one problem each.", href: "/data-persistence/how-to/" },
+ { eyebrow: "Look up", title: "`@netscript/database` reference", body: "Generated API reference. Related units: `kv`, `prisma-adapter-mysql`.", href: resolveXref("ref:database").href },
+ { eyebrow: "Understand", title: "Architecture", body: "The design rationale behind this pillar.", href: resolveXref("explain:architecture").href },
+] }) }}
diff --git a/docs/site/data-persistence/kv-queues-cron.md b/docs/site/data-persistence/kv-queues-cron.md
index cfc41f327..ffadd7af3 100644
--- a/docs/site/data-persistence/kv-queues-cron.md
+++ b/docs/site/data-persistence/kv-queues-cron.md
@@ -4,6 +4,7 @@ title: KV, queues & cron
templateEngine: [vento, md]
prev: { label: "Database & Prisma", href: "/data-persistence/database/" }
next: { label: "Telemetry & logging", href: "/observability/telemetry/" }
+order: 2
---
# KV, queues & cron
@@ -72,7 +73,7 @@ for you with no manual instrumentation.
{
title: "Do — Choose a queue provider",
body: "Recipe: pick between Deno KV, Redis, RabbitMQ, and PostgreSQL — and wire the Aspire-provisioned, local-fallback, and explicit-provider paths.",
- href: "/how-to/choose-a-queue-provider/",
+ href: "/data-persistence/how-to/choose-a-queue-provider/",
icon: "→"
},
{
@@ -353,7 +354,7 @@ for business-hours schedules. (5) always await scheduler.
{
title: "Do — Choose a queue provider",
body: "Task recipe: stand up each primitive in an existing workspace, with the Aspire-provisioned, local-fallback, and explicit-PostgreSQL paths spelled out.",
- href: "/how-to/choose-a-queue-provider/",
+ href: "/data-persistence/how-to/choose-a-queue-provider/",
icon: "◆"
},
{
diff --git a/docs/site/deno.lock b/docs/site/deno.lock
index c9ccaa43c..311255423 100644
--- a/docs/site/deno.lock
+++ b/docs/site/deno.lock
@@ -464,8 +464,10 @@
"https://deno.land/x/lume@v2.5.4/plugins/markdown.ts": "c7027605edee274762edb20f7040ccba6415c5fe656cc6e25ce91c448f467fd8",
"https://deno.land/x/lume@v2.5.4/plugins/modify_urls.ts": "137ba076b652f625b72de34692ea34b0fa5bbb5ba55cf7aa1ee789f2ce244bd5",
"https://deno.land/x/lume@v2.5.4/plugins/modules.ts": "d88ea1c1ab3bbbb501834f5409e5e00771a3dd4c9661b20e63445d1a80859257",
+ "https://deno.land/x/lume@v2.5.4/plugins/nav.ts": "d069569183b733866f8390943f3efda12e999985fc58390af5b7c418fc975c18",
"https://deno.land/x/lume@v2.5.4/plugins/pagefind.ts": "9b38d8ffb322d7fa4a4e9e8db36f6688835df00eb9901cebeb0b719ea65204fb",
"https://deno.land/x/lume@v2.5.4/plugins/paginate.ts": "7dfee977a205dfe0af33a3e406f73017badd2d4593cf27e5bd897da7ab12ba8a",
+ "https://deno.land/x/lume@v2.5.4/plugins/redirects.ts": "5c408b0de722c8a695f82e295625f881c4ed70b8e6a57a8aa5ecd3c5b4a9df31",
"https://deno.land/x/lume@v2.5.4/plugins/search.ts": "ff570560c6ca95598a1cbfb3a77611477ee7dbb53300bcc3ba14d18c9e5eba79",
"https://deno.land/x/lume@v2.5.4/plugins/toml.ts": "72c75546056e503a59752e33dc25542f2aa21d743bd47f498d722b97958212f5",
"https://deno.land/x/lume@v2.5.4/plugins/url.ts": "3718185697778f3b4dd17924d9d282d0a5a74030301e7fcae8a7f1b21f0ef9a9",
diff --git a/docs/site/how-to/build-a-validated-ingestion-queue.md b/docs/site/durable-workflows/how-to/build-a-validated-ingestion-queue.md
similarity index 83%
rename from docs/site/how-to/build-a-validated-ingestion-queue.md
rename to docs/site/durable-workflows/how-to/build-a-validated-ingestion-queue.md
index ca92960de..c62526e58 100644
--- a/docs/site/how-to/build-a-validated-ingestion-queue.md
+++ b/docs/site/durable-workflows/how-to/build-a-validated-ingestion-queue.md
@@ -2,8 +2,8 @@
layout: layouts/base.vto
title: Build a validated ingestion queue
templateEngine: [vento, md]
-prev: { label: "Build a server-validated form", href: "/how-to/build-a-server-validated-form/" }
-next: { label: "Publish a durable stream", href: "/how-to/publish-a-durable-stream/" }
+order: 101
+oldUrl: /how-to/build-a-validated-ingestion-queue/
---
# Build a validated ingestion queue
@@ -84,11 +84,11 @@ await queue.listen(async (message, context) => {
## Next steps
-- Compare providers in [Choose a queue provider](/how-to/choose-a-queue-provider/).
-- Use queues with KV and cron in [Queue / KV / cron](/how-to/queue-kv-cron/).
+- Compare providers in [Choose a queue provider](/data-persistence/how-to/choose-a-queue-provider/).
+- Use queues with KV and cron in [Queue / KV / cron](/data-persistence/how-to/queue-kv-cron/).
- Look up the full API in [queue reference](/reference/queue/).
{{ comp.nextPrev({
- prev: { label: "Build a server-validated form", href: "/how-to/build-a-server-validated-form/" },
- next: { label: "Publish a durable stream", href: "/how-to/publish-a-durable-stream/" }
+ prev: { label: "Build a server-validated form", href: "/web-layer/how-to/build-a-server-validated-form/" },
+ next: { label: "Publish a durable stream", href: "/durable-workflows/how-to/publish-a-durable-stream/" }
}) }}
diff --git a/docs/site/durable-workflows/how-to/index.md b/docs/site/durable-workflows/how-to/index.md
new file mode 100644
index 000000000..6769e47a8
--- /dev/null
+++ b/docs/site/durable-workflows/how-to/index.md
@@ -0,0 +1,12 @@
+---
+layout: layouts/base.vto
+title: Recipes
+templateEngine: [vento, md]
+order: 100
+---
+
+# Durable workflows — recipes
+
+Task-oriented recipes for this area. Each one solves a single concrete problem and assumes you know
+the basics from the guides above it in the sidebar. The full cross-area catalog lives at
+[All how-to recipes]({{ "howto:index" |> xref |> url }}).
diff --git a/docs/site/how-to/publish-a-durable-stream.md b/docs/site/durable-workflows/how-to/publish-a-durable-stream.md
similarity index 87%
rename from docs/site/how-to/publish-a-durable-stream.md
rename to docs/site/durable-workflows/how-to/publish-a-durable-stream.md
index 26b9fe35c..7c753de07 100644
--- a/docs/site/how-to/publish-a-durable-stream.md
+++ b/docs/site/durable-workflows/how-to/publish-a-durable-stream.md
@@ -2,8 +2,8 @@
layout: layouts/base.vto
title: Publish a durable stream
templateEngine: [vento, md]
-prev: { label: "Build a validated ingestion queue", href: "/how-to/build-a-validated-ingestion-queue/" }
-next: { label: "Restrict worker task permissions", href: "/how-to/restrict-worker-task-permissions/" }
+order: 102
+oldUrl: /how-to/publish-a-durable-stream/
---
# Publish a durable stream
@@ -107,6 +107,6 @@ addEventListener('unload', async () => {
- Look up the API in [streams reference](/reference/streams/).
{{ comp.nextPrev({
- prev: { label: "Build a validated ingestion queue", href: "/how-to/build-a-validated-ingestion-queue/" },
- next: { label: "Restrict worker task permissions", href: "/how-to/restrict-worker-task-permissions/" }
+ prev: { label: "Build a validated ingestion queue", href: "/durable-workflows/how-to/build-a-validated-ingestion-queue/" },
+ next: { label: "Restrict worker task permissions", href: "/background-processing/how-to/restrict-worker-task-permissions/" }
}) }}
diff --git a/docs/site/durable-workflows/index.md b/docs/site/durable-workflows/index.md
index c6b7dad0e..9281ba079 100644
--- a/docs/site/durable-workflows/index.md
+++ b/docs/site/durable-workflows/index.md
@@ -43,8 +43,17 @@ outcomes differ from a retry loop — read the
{{ comp.cardsGrid({ columns: 3, cards: [
{ eyebrow: "Overview & Concepts", title: "Durability model", body: "How saga state, trigger ingress, retries, and durable streams fit together.", href: "/explanation/durability-model/", icon: "O" },
{ eyebrow: "Quickstart", title: "Checkout saga", body: "Build a multi-step checkout flow in the Storefront tutorial.", href: "/tutorials/storefront/04-checkout-saga/", icon: "Q" },
- { eyebrow: "How-To", title: "Validated ingestion queue", body: "Validate incoming work before handing it to durable processing.", href: "/how-to/build-a-validated-ingestion-queue/", icon: "H" },
- { eyebrow: "How-To", title: "Durable stream", body: "Publish stream events for consumers.", href: "/how-to/publish-a-durable-stream/", icon: "H" },
+ { eyebrow: "How-To", title: "Validated ingestion queue", body: "Validate incoming work before handing it to durable processing.", href: "/durable-workflows/how-to/build-a-validated-ingestion-queue/", icon: "H" },
+ { eyebrow: "How-To", title: "Durable stream", body: "Publish stream events for consumers.", href: "/durable-workflows/how-to/publish-a-durable-stream/", icon: "H" },
{ eyebrow: "API Reference", title: "sagas", body: "Generated saga API symbols.", href: "/reference/sagas/", icon: "R" },
{ eyebrow: "API Reference", title: "triggers and streams", body: "Generated trigger and stream package symbols.", href: "/reference/triggers/", icon: "R" }
] }) }}
+
+## Learn, do, look up
+
+{{ comp.cardsGrid({ columns: 4, cards: [
+ { eyebrow: "Learn", title: "Storefront tutorial", body: "The checkout saga chapter models a multi-step flow with compensation.", href: resolveXref("tut:storefront").href },
+ { eyebrow: "Do", title: "Recipes", body: "Task-oriented recipes for this area, one problem each.", href: "/durable-workflows/how-to/" },
+ { eyebrow: "Look up", title: "`@netscript/sagas` reference", body: "Generated API reference. Related units: `triggers`, `streams`.", href: resolveXref("ref:sagas").href },
+ { eyebrow: "Understand", title: "The durability model", body: "The design rationale behind this pillar.", href: resolveXref("explain:durability-model").href },
+] }) }}
diff --git a/docs/site/durable-workflows/sagas.md b/docs/site/durable-workflows/sagas.md
index f1f4ab7d7..5cca960a3 100644
--- a/docs/site/durable-workflows/sagas.md
+++ b/docs/site/durable-workflows/sagas.md
@@ -4,6 +4,7 @@ title: Durable sagas
templateEngine: [vento, md]
prev: { label: "Background jobs", href: "/background-processing/workers/" }
next: { label: "Triggers & ingress", href: "/durable-workflows/triggers/" }
+order: 1
---
# Durable sagas
diff --git a/docs/site/durable-workflows/streams.md b/docs/site/durable-workflows/streams.md
index 354de3fc7..565d5112c 100644
--- a/docs/site/durable-workflows/streams.md
+++ b/docs/site/durable-workflows/streams.md
@@ -4,6 +4,7 @@ title: Durable streams
templateEngine: [vento, md]
prev: { label: "Triggers & ingress", href: "/durable-workflows/triggers/" }
next: { label: "Database & Prisma", href: "/data-persistence/database/" }
+order: 2
---
# Durable streams
@@ -92,7 +93,7 @@ report, which is as useful to a CLI doctor or a coding agent as it is to a test.
{
title: "Do — add the streams plugin",
body: "Public package install adds the stream plugin dependency and user-owned glue; local netscript-dev scaffolding supports contributor-source samples.",
- href: "/how-to/add-a-plugin/",
+ href: "/orchestration-runtime/how-to/add-a-plugin/",
icon: "◆"
},
{
diff --git a/docs/site/durable-workflows/triggers.md b/docs/site/durable-workflows/triggers.md
index 15ac1c921..4cb5c2ef4 100644
--- a/docs/site/durable-workflows/triggers.md
+++ b/docs/site/durable-workflows/triggers.md
@@ -4,6 +4,7 @@ title: Triggers & ingress
templateEngine: [vento, md]
prev: { label: "Durable sagas", href: "/durable-workflows/sagas/" }
next: { label: "Durable streams", href: "/durable-workflows/streams/" }
+order: 3
---
# Triggers & ingress
@@ -73,7 +74,7 @@ processor/ingress runtime (`@netscript/plugin-triggers-core/runtime`).
{
title: "Do — Add a plugin",
body: "The how-to recipe for public plugin package install and the local netscript-dev contributor path for source-backed trigger samples.",
- href: "/how-to/add-a-plugin/",
+ href: "/orchestration-runtime/how-to/add-a-plugin/",
icon: "◆"
}
] }) }}
diff --git a/docs/site/explanation/architecture.md b/docs/site/explanation/architecture.md
index 8f89361e8..0d073b59a 100644
--- a/docs/site/explanation/architecture.md
+++ b/docs/site/explanation/architecture.md
@@ -4,6 +4,7 @@ title: Architecture
templateEngine: [vento, md]
prev: { label: "Explanation", href: "/explanation/" }
next: { label: "Contracts & type flow", href: "/explanation/contracts/" }
+order: 1
---
# The NetScript architecture
diff --git a/docs/site/explanation/aspire.md b/docs/site/explanation/aspire.md
index 35920566b..b93c8ff7b 100644
--- a/docs/site/explanation/aspire.md
+++ b/docs/site/explanation/aspire.md
@@ -4,6 +4,7 @@ title: Orchestration with Aspire
templateEngine: [vento, md]
prev: { label: "Observability", href: "/explanation/observability/" }
next: { label: "Capabilities", href: "/capabilities/" }
+order: 7
---
# Orchestration with Aspire
diff --git a/docs/site/explanation/auth-model.md b/docs/site/explanation/auth-model.md
index e506c24e3..dcb81772b 100644
--- a/docs/site/explanation/auth-model.md
+++ b/docs/site/explanation/auth-model.md
@@ -4,17 +4,20 @@ title: The pure-backend auth model
templateEngine: [vento, md]
prev: { label: "The plugin system", href: "/explanation/plugin-system/" }
next: { label: "Durability model", href: "/explanation/durability-model/" }
+order: 4
---
# The pure-backend auth model
+*This essay is the **why** — the model and its trade-offs. The day-to-day *how* (guides, recipes, API) lives in **Build › [Identity & Access](/identity-access/)**.*
+
This page explains *what* NetScript authentication actually is, *why* it is designed as a
**pure-backend seam** rather than a built-in identity provider, and *how* a single typed port lets
you swap GitHub OAuth for WorkOS or better-auth without touching one line of application code. It is
understanding-oriented — read it to build the mental model before you wire authentication into a
project. When you want the headline API and endpoints, see the
[auth capability](/capabilities/auth/); when you want to do the task, follow
-[Add authentication](/how-to/add-authentication/); when you want exact exported symbols, follow
+[Add authentication](/identity-access/how-to/add-authentication/); when you want exact exported symbols, follow
[`reference/service/`](/reference/service/) and the auth adapter references it links to.
{{ comp callout { type: "important", title: "Alpha status" } }}
@@ -248,7 +251,7 @@ boundary, you own the logic) — authentication is just the identity-shaped inst
- **The capability:** [Authentication](/capabilities/auth/) — the headline API, the `auth-api`
service on :8094, the five endpoints, and the backend matrix at a glance.
-- **Do it:** [Add authentication](/how-to/add-authentication/) — add the `auth` plugin, choose a
+- **Do it:** [Add authentication](/identity-access/how-to/add-authentication/) — add the `auth` plugin, choose a
backend with `NETSCRIPT_AUTH_BACKEND`, run the migration, and wire provider env.
- **Related model:** [The plugin system](/explanation/plugin-system/) — why a thin plugin composes a
pure capability, the same shape this auth seam follows.
diff --git a/docs/site/explanation/contracts.md b/docs/site/explanation/contracts.md
index b2d0a03b1..7cc34f2d4 100644
--- a/docs/site/explanation/contracts.md
+++ b/docs/site/explanation/contracts.md
@@ -4,6 +4,7 @@ title: Contracts & type flow
templateEngine: [vento, md]
prev: { label: "Architecture", href: "/explanation/architecture/" }
next: { label: "The plugin system", href: "/explanation/plugin-system/" }
+order: 2
---
# Contracts & type flow
diff --git a/docs/site/explanation/durability-model.md b/docs/site/explanation/durability-model.md
index c21fd1589..8cdf97d72 100644
--- a/docs/site/explanation/durability-model.md
+++ b/docs/site/explanation/durability-model.md
@@ -4,10 +4,13 @@ title: Durability model
templateEngine: [vento, md]
prev: { label: "Auth model", href: "/explanation/auth-model/" }
next: { label: "Observability", href: "/explanation/observability/" }
+order: 5
---
# Durability model
+*This essay is the **why** — the model and its trade-offs. The day-to-day *how* (guides, recipes, API) lives in **Build › [Durable workflows](/durable-workflows/)**.*
+
This essay is **understanding-oriented**. It answers one question: *how does NetScript make
long-running, message-driven work survive a process restart?* It builds the mental model — what a
saga is, how its state is persisted, where that state physically lives, and how compensation and
diff --git a/docs/site/explanation/observability.md b/docs/site/explanation/observability.md
index a6a05a353..81828ded8 100644
--- a/docs/site/explanation/observability.md
+++ b/docs/site/explanation/observability.md
@@ -4,16 +4,19 @@ title: Observability
templateEngine: [vento, md]
prev: { label: "Durability model", href: "/explanation/durability-model/" }
next: { label: "Orchestration with Aspire", href: "/explanation/aspire/" }
+order: 6
---
# Observability
+*This essay is the **why** — the model and its trade-offs. The day-to-day *how* (guides, recipes, API) lives in **Build › [Observability](/observability/)**.*
+
This essay answers one question: how does NetScript make a *distributed, multi-process*
application observable, so that one logical operation reads as one story even though it crosses
HTTP, a queue, a saga, and a worker subprocess? The answer is a single idea applied everywhere —
**the trace context travels with the work** — wired into the framework boundaries so you inherit
it for free. Read this to build the mental model; to wire spans yourself, follow the
-[how-to: add OpenTelemetry](/how-to/add-opentelemetry/); for the headline API and ports, see the
+[how-to: add OpenTelemetry](/observability/how-to/add-opentelemetry/); for the headline API and ports, see the
{{ comp.xref({ key: "cap:telemetry" }) }} hub; for exact exported symbols, see
{{ comp.xref({ key: "ref:telemetry" }) }}.
@@ -272,7 +275,7 @@ the same span model.
## Where to go next
-- **Do it:** the [how-to: add OpenTelemetry](/how-to/add-opentelemetry/) walks adding a custom span,
+- **Do it:** the [how-to: add OpenTelemetry](/observability/how-to/add-opentelemetry/) walks adding a custom span,
structured logs, and `traceparent` propagation against a running service.
- **Hub:** the {{ comp.xref({ key: "cap:telemetry" }) }} hub covers the headline API and the
OTel-wired-into-boundaries model with the real endpoints; {{ comp.xref({ key: "cap:auth" }) }}
diff --git a/docs/site/explanation/plugin-system.md b/docs/site/explanation/plugin-system.md
index 2a279d017..e7dea879d 100644
--- a/docs/site/explanation/plugin-system.md
+++ b/docs/site/explanation/plugin-system.md
@@ -4,6 +4,7 @@ title: The plugin system
templateEngine: [vento, md]
prev: { label: "Contracts & type flow", href: "/explanation/contracts/" }
next: { label: "Auth model", href: "/explanation/auth-model/" }
+order: 3
---
# The plugin system
@@ -269,7 +270,7 @@ and topics through the same vocabulary — each running as its own isolated reso
## Where to go next
{{ comp.featureGrid({ items: [
- { title: "Add a first-party plugin", body: "The task-oriented recipe: scaffold, register, regenerate, verify.", href: "/how-to/add-a-plugin/", icon: "🔌" },
+ { title: "Add a first-party plugin", body: "The task-oriented recipe: scaffold, register, regenerate, verify.", href: "/orchestration-runtime/how-to/add-a-plugin/", icon: "🔌" },
{ title: "The auth model", body: "The five-unit core/adapter/plugin split — the model at its richest.", href: "/explanation/auth-model/", icon: "🔐" },
{ title: "Orchestration with Aspire", body: "How the AppHost turns manifests into running API and background resources.", href: "/explanation/aspire/", icon: "🧩" },
{ title: "Plugin authoring contract", body: "definePlugin(), the contribution axes, and the registry emitter.", href: "/reference/plugin/", icon: "📐" }
diff --git a/docs/site/glossary.md b/docs/site/glossary.md
index 527abac06..31cb722e2 100644
--- a/docs/site/glossary.md
+++ b/docs/site/glossary.md
@@ -37,7 +37,7 @@ These are the words you will meet first — in the [Quickstart](/quickstart/), t
{{ comp.apiTable({ caption: "A–C", rows: [
{ name: "AppHost", type: "aspire/apphost.mts", desc: "The Aspire orchestration entry point that boots your whole workspace — Postgres, Redis, services, and plugin processors — as one resource graph with a dashboard at https://localhost:18888. In NetScript the AppHost is a generated Node/TypeScript file (aspire/apphost.mts) driven by aspire.config.json and appsettings.json, not a dotnet project. You start it with cd aspire && aspire start, and it must be up before any netscript db command. See Explanation → Aspire and reference/aspire/." },
{ name: "archetype", type: "doctrine classification", desc: "The architectural category a package or plugin belongs to (for example a contract unit, an adapter, a runtime, or a plugin), which determines its public surface, quality gates, and allowed dependencies. Archetype is a framework-internal doctrine term; as an app author you mostly meet it indirectly through the shape of a scaffolded plugin. See Explanation → Plugin system." },
- { name: "Aspire", type: "aspire start", desc: "The .NET Aspire orchestration layer NetScript uses for local development. It provisions your database (Postgres is the recommended engine; or mysql / mssql via --db, each an Aspire container resource — sqlite is file-backed and adds no container) and Redis as Docker containers and wires every service and plugin processor together with health, logs, and OTLP traces — no manual docker compose. The escape hatch is netscript init --no-aspire for a leaner single-process loop. See Explanation → Aspire and How-to → Deploy." },
+ { name: "Aspire", type: "aspire start", desc: "The .NET Aspire orchestration layer NetScript uses for local development. It provisions your database (Postgres is the recommended engine; or mysql / mssql via --db, each an Aspire container resource — sqlite is file-backed and adds no container) and Redis as Docker containers and wires every service and plugin processor together with health, logs, and OTLP traces — no manual docker compose. The escape hatch is netscript init --no-aspire for a leaner single-process loop. See Explanation → Aspire and How-to → Deploy." },
{ name: "AuthBackendPort", type: "@netscript/plugin-auth-core/ports", desc: "The single seam every authentication backend implements — defined by @netscript/plugin-auth-core, it composes provider, session-store, crypto, and principal-mapper sub-ports plus an authenticate(request) method and an optional interactive flow. Backends are pure adapters behind this port; the host never special-cases a vendor. See Capabilities → Authentication and Explanation → Auth model." },
{ name: "AuthBackendOperationUnsupportedError", type: "typed capability boundary", desc: "The typed error a backend throws when you call an operation it does not implement — for example a session mutation on a stateless backend, or an interactive sign-in on a non-interactive one. It makes the single-active-backend capability matrix fail loud rather than silently no-op, so missing surface is a visible error, not a mystery. See Explanation → Auth model and the Capabilities → Auth export list." },
{ name: "AuthSession", type: "@netscript/plugin-auth-core/domain", desc: "The domain record for an authenticated session — id, user, account, and state (active | expired | revoked) — defined as a Zod-validated type in @netscript/plugin-auth-core. It is the durable shape the session store reads and writes and the entity the auth stream projects. See Explanation → Auth model and Capabilities → Authentication." },
@@ -59,7 +59,7 @@ These are the words you will meet first — in the [Quickstart](/quickstart/), t
{ name: "manifest", type: "plugins//mod.ts", desc: "A plugin's public face — its mod.ts — which exports the plugin object (for example workersPlugin or authPlugin), an inspector (inspectWorkers, inspectAuth), and the contribution types the host discovers. The composition root references each plugin by its manifest path, and a generated registry aggregates them. See Explanation → Plugin system." },
{ name: "oRPC", type: "@orpc/contract · @orpc/server", desc: "The contract-and-RPC toolkit (@orpc/*, pinned at ^1.14.6) that underpins NetScript's typed APIs: contracts are declared with oc.route().input().output(), implemented with implement(...), served by defineService / createService, and consumed by a fully typed client. Workers, sagas, the auth service, and triggers all expose oRPC (mounted under /api/rpc/*); triggers' only exception is the raw, HMAC-verifying webhook ingress endpoint. See Explanation → Contracts." },
{ name: "opaque session token (HMAC)", type: "createHmacSessionTokenCrypto(secret)", desc: "The default session-token scheme in the auth core — a WebCrypto HMAC-SHA256 token that carries no readable claims (opaque to the client) and is verified server-side against a secret. It is what the AuthSessionCryptoPort produces by default, so a stolen token reveals nothing and cannot be forged without the secret. See Explanation → Auth model and Capabilities → Auth." },
- { name: "plugin", type: "package + generated glue", desc: "An installable, thread-isolated capability. Public workspaces install plugin packages with commands such as netscript plugin install @netscript/plugin-workers, which add the dependency and emit user-owned glue/sample files that import the package; local-source contributor samples use deno run -A packages/cli/bin/netscript-dev.ts plugin install worker --name workers --samples. The plugin's contracts, services, runtime entrypoints, and Prisma schema stay in the installed package unless you are authoring or syncing full source. See Explanation → Plugin system and How-to → Add a plugin." },
+ { name: "plugin", type: "package + generated glue", desc: "An installable, thread-isolated capability. Public workspaces install plugin packages with commands such as netscript plugin install @netscript/plugin-workers, which add the dependency and emit user-owned glue/sample files that import the package; local-source contributor samples use deno run -A packages/cli/bin/netscript-dev.ts plugin install worker --name workers --samples. The plugin's contracts, services, runtime entrypoints, and Prisma schema stay in the installed package unless you are authoring or syncing full source. See Explanation → Plugin system and How-to → Add a plugin." },
{ name: "Principal", type: "@netscript/service/auth", desc: "The authenticated identity an authenticator resolves from a request — id, scopes, and a scheme ('api-key' | 'bearer' | 'trusted-header' | 'custom'). The auth-plugin backends map their sessions to a Principal with scheme: 'custom', so service-layer authz treats vendor-backed identities uniformly. See Explanation → Auth model." }
] }) }}
@@ -83,7 +83,7 @@ the data layer, the auth seams, and the moving parts under a running plugin.
{{ comp.apiTable({ caption: "Infrastructure, data & auth internals", rows: [
{ name: "appsettings.json", type: "infra config", desc: "The root infrastructure manifest the Aspire AppHost actually reads — declaring NetScript.Databases (the database engine — Postgres is the recommended engine, or mysql / mssql / sqlite selected at scaffold with --db — container mode, primary database), NetScript.Cache (the redis cache by default; garnet or deno-kv via --cache-backend), the services, and the per-plugin Workdirs. It is also where backend selections like sagas.store.backend and auth.backend can live. Note that netscript.config.ts's databases block is intentionally near-empty; the live DB/cache config lives here. See Explanation → Aspire." },
- { name: "auth backend", type: "@netscript/auth-*", desc: "A pure adapter that implements AuthBackendPort: kv-oauth (interactive OAuth/OIDC redirect flow, KV sessions — the only one exposing InteractiveFlowPort), workos (AuthKit sealed cookie, non-interactive), and better-auth (Prisma-backed, non-interactive). The plugin composes exactly one — see single-active-backend — and serves the unified auth oRPC surface on port :8094. See Explanation → Auth model and How-to → Add authentication." },
+ { name: "auth backend", type: "@netscript/auth-*", desc: "A pure adapter that implements AuthBackendPort: kv-oauth (interactive OAuth/OIDC redirect flow, KV sessions — the only one exposing InteractiveFlowPort), workos (AuthKit sealed cookie, non-interactive), and better-auth (Prisma-backed, non-interactive). The plugin composes exactly one — see single-active-backend — and serves the unified auth oRPC surface on port :8094. See Explanation → Auth model and How-to → Add authentication." },
{ name: "background processor", type: "bin/combined.ts", desc: "The separate, long-running process that executes a plugin's work, distinct from its HTTP API service. Workers run from bin/combined.ts; sagas run from src/runtime/saga-runner.ts; triggers run from src/runtime/trigger-processor.ts. The AppHost starts these as their own Aspire resources. See Explanation → Plugin system." },
{ name: "Garnet", type: "Cache.garnet (KV)", desc: "One of the Redis-compatible cache/KV backends Aspire can provision as a container alongside Postgres (the default is redis; select Garnet with --cache-backend garnet), used for executions, saga registry metadata, kv-oauth sessions, and Deno KV-backed state. See Capabilities → KV / queues / cron and reference/kv/." },
{ name: "OTel / observability", type: "@netscript/telemetry", desc: "OpenTelemetry tracing and structured logging wired into the framework. Job dispatch, execution, step events, progress, scheduler runs, and subprocess trace continuation emit real spans — traces show up in Aspire automatically (OTLP endpoint at http://localhost:4318). The one remaining gap is the scaffold createJobTools(ctx) handler helpers (trace.addEvent / withChildSpan / progress), which are still no-op stubs (tracked debt, fix planned) — for custom handler spans call @netscript/telemetry helpers directly. See Explanation → Observability and Capabilities → Telemetry." },
diff --git a/docs/site/how-to/index.md b/docs/site/how-to/index.md
index 38911a8a5..0bd41cdbb 100644
--- a/docs/site/how-to/index.md
+++ b/docs/site/how-to/index.md
@@ -1,99 +1,136 @@
---
layout: layouts/base.vto
-title: How-to guides
+title: All recipes
templateEngine: [vento, md]
prev: null
-next: { label: "Add a plugin", href: "/how-to/add-a-plugin/" }
+next: { label: "Add a plugin", href: "/orchestration-runtime/how-to/add-a-plugin/" }
+nav_hide: true
---
-How-to guides are **goal-first recipes**: each one starts from a concrete
-intent — *"I need to add a service,"* *"I need auth,"* *"I need this to deploy
-without Aspire"* — and gives you the shortest reliable path from that intent to a
-working, verified change. They assume you already have a NetScript workspace and
-know the basics; they do **not** re-teach the framework.
+# All recipes
-If NetScript is new to you, start with the [tutorials](/tutorials/) — they build
-one continuous application from zero. For exact API signatures, use the
-[reference](/reference/). For the concepts *behind* a task — why services are
-contracts-first, what "durable" means, how Aspire wires dependencies — read the
-[explanation](/explanation/) pages. Each recipe links back to the capability hub
-and reference that go deeper.
+Recipes live inside their pillar in the sidebar — each area's **Recipes** section
+sits below the guides that teach the basics it assumes. This page is the
+cross-area index: every recipe in the docs, grouped by pillar, so you can scan
+the whole catalog in one place. Each one is goal-first — it starts from a
+concrete intent and ends with a command that proves the change works.
{{ comp callout { tone: "info", title: "One prerequisite spans almost every recipe" } }}
Anything that touches Postgres, Redis/Garnet, or a plugin service expects Aspire
to be running first. From your workspace: cd aspire && aspire start
brings up the dependencies and the dashboard on https://localhost:18888
-before any netscript db command or service call. The
-recipes call this out where it matters, but it is the single most common missing
-step.
+before any netscript db command or service call.
+The recipes call this out where it matters, but it is the single most common
+missing step.
{{ /comp }}
-## Build & extend a workspace
-
-These recipes add capabilities to an existing workspace and verify the wiring.
-Each emits the relevant workspace-owned glue, samples, or service files and ends with a
-command you can run to confirm it works.
-
-{{ comp.featureGrid({ items: [
- { title: "Add a plugin", body: "Install a first-party plugin through the public package flow, or use the local netscript-dev path for contributor-source samples. Emits user-owned glue, regenerates the registry, and verifies the service answers on its port.", href: "/how-to/add-a-plugin/" },
- { title: "Add a service", body: "Stand up a new typed oRPC service: define an @orpc/contract + zod contract, implement() the handlers, serve it with defineService(...) one-shot or createService(...).serve() fluent, and confirm it answers on /api/rpc/*.", href: "/how-to/add-a-service/" },
- { title: "Add authentication", body: "Add the official auth plugin (auth-api on :8094, five endpoints under /api/v1/auth/*). Pick one active backend via NETSCRIPT_AUTH_BACKEND — kv-oauth (interactive, default), WorkOS, or better-auth — run the auth.prisma migration, and sign in.", href: "/how-to/add-authentication/" },
- { title: "Database & migration", body: "Initialize, generate, seed, and inspect the Postgres schema: netscript db init --name init → db generate → db seed → db status. Requires aspire start first so Postgres is provisioned.", href: "/how-to/database-migration/" }
-] }) }}
-
-## Set up the developer environment
-
-Recipes for the toolchain around a NetScript workspace: editor intelligence, orchestration, and
-runtime setup that should behave the same on every device.
-
-{{ comp.featureGrid({ items: [
- { title: "Deno LSP code intelligence", body: "Install the Claude Code Deno LSP plugin, enable the LSP tool globally, and keep go-to-definition, hover, references, symbols, and diagnostics aligned across CLI, VS Code, and Zed.", href: "/how-to/deno-lsp-code-intelligence/" },
- { title: "Deploy locally with Aspire", body: "Run the full local resource graph from the generated Aspire AppHost: dashboard, infrastructure, services, plugin APIs, and background processors.", href: "/how-to/deploy-local-aspire/" }
-] }) }}
-
-## Wire primitives & observability
-
-Recipes for the shared building blocks every plugin leans on — queues, KV, cron,
-and the OpenTelemetry traces that make them visible in the Aspire dashboard.
-
-{{ comp.featureGrid({ items: [
- { title: "Queue / KV / cron", body: "Use the reactive KV store, the durable queue (four backends — RabbitMQ, Redis, Deno KV, and explicit-provider PostgreSQL), and cron schedules. Covers --unstable-kv and the auto-discovery order vs. an explicit provider:'postgres'.", href: "/how-to/queue-kv-cron/" },
- { title: "Add OpenTelemetry", body: "Emit custom spans and structured logs with @netscript/telemetry helpers, propagate traceparent across services, and read the traces that land in the Aspire dashboard. Worker job dispatch/execution traces are already real and automatic.", href: "/how-to/add-opentelemetry/" }
-] }) }}
-
-## Ship the UI & deploy
-
-Recipes for the front end and for taking a workspace to production — including
-the Aspire-free portability path.
-
-{{ comp.featureGrid({ items: [
- { title: "Customize the Fresh UI", body: "Bring in and own the dashboard UI with the ui:init / ui:add tasks. The scaffold uses copy-source ownership — the components land in your workspace, so you edit them directly rather than depending on a hidden package.", href: "/how-to/customize-fresh-ui/" },
- { title: "Build a durable chat", body: "Wire an AI chat onto a Fresh route whose transcript survives reload and reconnect via a durable session stream; hydrate the fresh-ui chat components; and add one server-side tool.", href: "/how-to/build-a-durable-chat/" },
- { title: "Deploy", body: "The portability and config story: the raw deno task entry points behind each service, plus the --no-aspire escape hatch when you provision Postgres and Redis (or Garnet) yourself. Docker, Compose, and Linux targets are config-only scaffolding today, not runnable deploy verbs — for a first-class hosted path, see Deploy to Deno Deploy.", href: "/how-to/deploy/" },
- { title: "Deploy to Deno Deploy", body: "Push a workspace to Deno Deploy with the first-class netscript deploy deno-deploy plan | up | down | status | logs command: preflight the unstable-API guard, push a preview, promote to prod, and read status and logs.", href: "/how-to/deploy-deno-deploy/" },
- { title: "Author a plugin", body: "Advanced: build a custom plugin from scratch. Defines the scaffold.plugin.json provider kind, the manifest exports, and the mod.ts contract the host discovers — the same shape the first-party plugins use.", href: "/how-to/author-a-plugin/" }
-] }) }}
-
-## How a recipe is shaped
-
-Every how-to page follows the same contract so you always know where to look:
-
-- **Goal** — one sentence stating exactly what you will have when you finish.
-- **Prerequisites** — the workspace state and running dependencies the recipe
- assumes (almost always including a live `aspire start`).
-- **Steps** — added-lines code blocks, annotated with the file path they belong
- in, using the public `netscript ` command form throughout.
-- **Production pitfalls** — the caveats that bite in real deployments, stated
- plainly rather than glossed over.
-- **See also** — the capability hub, reference page, and related recipes that
- take the topic further.
-
-{{ comp callout { tone: "note", title: "Pick by intent, not by feature name" } }}
-If you are not sure which recipe you want, name the outcome first. "Users
-must sign in" → Add authentication.
-"This event should fan out to a background job" →
-Add a plugin (triggers + workers).
-"It has to run where there is no Aspire" →
-Deploy. The recipes are deliberately small and
-composable; most real features chain two or three of them.
-{{ /comp }}
+## Web Layer
+
+- [Customize the Fresh UI]({{ "howto:customize-fresh-ui" |> xref |> url }}) —
+ bring the dashboard components into your workspace with `ui:init` / `ui:add`
+ and edit them directly.
+- [Build a server-validated form]({{ "howto:build-a-server-validated-form" |> xref |> url }}) —
+ route-bound form state, server validation, mutation, and success handling in
+ one typed page definition with `definePage().withForm()`.
+- [Build a desktop frontend]({{ "howto:build-a-desktop-frontend" |> xref |> url }}) —
+ one Fresh frontend that runs as an ordinary browser app and gains native
+ capabilities on the desktop.
+
+## Services & SDK
+
+- [Add a service]({{ "howto:add-a-service" |> xref |> url }}) — define a typed
+ contract, implement the handlers, and confirm the service answers on
+ `/api/rpc/*`.
+- [Discover services]({{ "howto:discover-services" |> xref |> url }}) — call
+ another plugin's or workspace member's typed service without hardcoding its
+ address.
+- [Expose OpenAPI & Scalar]({{ "howto:expose-openapi-scalar" |> xref |> url }}) —
+ publish an OpenAPI document and browsable Scalar docs for a service.
+
+## Background jobs
+
+- [Run a polyglot task]({{ "howto:run-a-polyglot-task" |> xref |> url }}) —
+ define a non-TypeScript script (Python, shell, .NET, any executable) as a
+ task with a permission sandbox and run it through the executor.
+- [Tune the worker runtime]({{ "howto:tune-worker-runtime" |> xref |> url }}) —
+ trade throughput against isolation with concurrency, runner mode, per-task
+ permissions, and timeouts/retries.
+- [Restrict worker task permissions]({{ "howto:restrict-worker-task-permissions" |> xref |> url }}) —
+ give every Deno task explicit permissions; an omitted permission object
+ compiles to `--allow-all`.
+- [Add a task runtime adapter]({{ "howto:add-a-task-runtime-adapter" |> xref |> url }}) —
+ advanced: add a custom runtime adapter to the built-in task executor.
+
+## Durable workflows
+
+- [Build a validated ingestion queue]({{ "howto:build-a-validated-ingestion-queue" |> xref |> url }}) —
+ a typed queue whose messages are schema-checked before they enter the queue
+ and again before a consumer handles them.
+- [Publish a durable stream]({{ "howto:publish-a-durable-stream" |> xref |> url }}) —
+ let server-side state be subscribed to by browsers and other consumers
+ through a durable stream service.
+
+## AI & Agents
+
+- [Build a durable chat]({{ "howto:build-a-durable-chat" |> xref |> url }}) —
+ an AI chat on a Fresh route whose transcript survives reload and reconnect,
+ with one server-side tool.
+
+## Data & Persistence
+
+- [Database & migration]({{ "howto:database-migration" |> xref |> url }}) —
+ initialize, generate, seed, and inspect the Postgres schema with the
+ `netscript db` commands.
+- [Queue / KV / cron]({{ "howto:queue-kv-cron" |> xref |> url }}) — the reactive
+ KV store, the durable queue, and cron schedules, including `--unstable-kv`.
+- [Choose a queue provider]({{ "howto:choose-a-queue-provider" |> xref |> url }}) —
+ pick the right queue backend, and either let auto-discovery select one or pin
+ one explicitly.
+- [Use a second database]({{ "howto:use-a-second-database" |> xref |> url }}) —
+ add a second Postgres, or a MySQL/SQL Server instance, alongside the default
+ database.
+
+## Identity & Access
+
+- [Add authentication]({{ "howto:add-authentication" |> xref |> url }}) — install
+ the official auth plugin, pick one active backend via
+ `NETSCRIPT_AUTH_BACKEND`, migrate, and sign in.
+
+## Orchestration & Runtime
+
+- [Add a plugin]({{ "howto:add-a-plugin" |> xref |> url }}) — install a
+ first-party plugin, regenerate the registry, and verify the service answers.
+- [Deploy locally with Aspire]({{ "howto:deploy-local-aspire" |> xref |> url }}) —
+ run the full local resource graph from the generated Aspire AppHost.
+- [Deploy]({{ "howto:deploy" |> xref |> url }}) — the portability story: raw
+ `deno task` entry points and the `--no-aspire` escape hatch when you provision
+ dependencies yourself.
+- [Deploy to Deno Deploy]({{ "howto:deploy-deno-deploy" |> xref |> url }}) —
+ push a preview, promote to prod, and read status and logs with the
+ first-class deploy command.
+- [Graceful shutdown]({{ "howto:graceful-shutdown" |> xref |> url }}) — drain
+ in-flight requests and jobs, run teardown hooks, and close connections on
+ `SIGINT`/`SIGTERM`.
+- [Roll out runtime overrides]({{ "howto:roll-out-runtime-overrides" |> xref |> url }}) —
+ change a deployed behavior without rebuilding the workspace.
+- [Author a plugin]({{ "howto:author-a-plugin" |> xref |> url }}) — advanced:
+ build a custom plugin with the same manifest and `mod.ts` contract the
+ first-party plugins use.
+- [Deno LSP code intelligence]({{ "howto:deno-lsp-code-intelligence" |> xref |> url }}) —
+ keep go-to-definition, hover, and diagnostics aligned across CLI and editors.
+
+## Observability
+
+- [Add OpenTelemetry]({{ "howto:add-opentelemetry" |> xref |> url }}) — emit
+ custom spans and structured logs, propagate `traceparent`, and read the
+ traces in the Aspire dashboard.
+
+---
+
+Not sure which recipe you want? Name the outcome first. *"Users must sign in"* →
+[Add authentication]({{ "howto:add-authentication" |> xref |> url }}).
+*"This event should fan out to a background job"* →
+[Add a plugin]({{ "howto:add-a-plugin" |> xref |> url }}).
+*"It has to run where there is no Aspire"* →
+[Deploy]({{ "howto:deploy" |> xref |> url }}). The recipes are deliberately
+small and composable; most real features chain two or three of them.
diff --git a/docs/site/identity-access/auth.md b/docs/site/identity-access/auth.md
index 558dd44ee..1e74eabc6 100644
--- a/docs/site/identity-access/auth.md
+++ b/docs/site/identity-access/auth.md
@@ -4,6 +4,7 @@ title: Authentication
templateEngine: [vento, md]
prev: { label: "Fresh UI & design", href: "/web-layer/fresh-ui/" }
next: null
+order: 1
---
# Authentication
@@ -42,7 +43,7 @@ or OIDC provider — a typed /api/v1/auth/* surface your s
UI can call to sign users in, resolve the current session, and sign out. The default
kv-oauth backend gives you a complete interactive redirect flow with KV-stored
sessions and an opaque HMAC-signed session token. To wire it into a workspace step by step, see
-Add authentication; to understand the pure-backend
+Add authentication; to understand the pure-backend
port model and why only one backend is active, see
The authentication model.
{{ /comp }}
@@ -106,7 +107,7 @@ and how `InteractiveFlowPort` gates the redirect flow — is in
{
title: "Do — Add authentication",
body: "Task recipe: add the auth plugin, set NETSCRIPT_AUTH_BACKEND, run the auth.prisma migration, and wire kv-oauth with a provider preset.",
- href: "/how-to/add-authentication/",
+ href: "/identity-access/how-to/add-authentication/",
icon: "◆"
},
{
@@ -296,7 +297,7 @@ better-auth-shaped models — `User` → `auth_users`, `Session` → `auth_sessi
`Account` → `auth_accounts`, `Verification` → `auth_verifications`. These tables back the
better-auth adapter; `kv-oauth` keeps its sessions in Deno KV, and WorkOS is stateless. As with
every plugin schema, you bring the tables to life by running the database workflow **after Aspire
-is up** — see [Database migrations](/how-to/database-migration/).
+is up** — see [Database migrations](/data-persistence/how-to/database-migration/).
The plugin also emits five durable `auth.*` runtime events through the durable-streams runtime —
the `AUTH_STREAM_EVENT_TYPES`: `auth.signin.started`, `auth.signin.failed`,
@@ -375,7 +376,7 @@ adapters now have dedicated generated reference pages.
{
title: "Do — Add authentication",
body: "Task recipe: add the auth plugin, set NETSCRIPT_AUTH_BACKEND, run the auth.prisma migration, and wire kv-oauth with a provider preset.",
- href: "/how-to/add-authentication/",
+ href: "/identity-access/how-to/add-authentication/",
icon: "◆"
},
{
diff --git a/docs/site/identity-access/better-auth-plugins.md b/docs/site/identity-access/better-auth-plugins.md
index ddcd86495..65787fb08 100644
--- a/docs/site/identity-access/better-auth-plugins.md
+++ b/docs/site/identity-access/better-auth-plugins.md
@@ -2,6 +2,7 @@
layout: layouts/base.vto
title: better-auth plugins
templateEngine: [vento, md]
+order: 2
---
# better-auth plugins
diff --git a/docs/site/how-to/add-authentication.md b/docs/site/identity-access/how-to/add-authentication.md
similarity index 99%
rename from docs/site/how-to/add-authentication.md
rename to docs/site/identity-access/how-to/add-authentication.md
index c0cbe89c3..5ce355e39 100644
--- a/docs/site/how-to/add-authentication.md
+++ b/docs/site/identity-access/how-to/add-authentication.md
@@ -2,8 +2,8 @@
layout: layouts/base.vto
title: Add authentication
templateEngine: [vento, md]
-prev: { label: "Author a plugin", href: "/how-to/author-a-plugin/" }
-next: null
+order: 101
+oldUrl: /how-to/add-authentication/
---
# Add authentication
@@ -157,7 +157,7 @@ netscript db seed # optional seed data
netscript db status # confirm the migration is applied
```
-See [Run a database migration](/how-to/database-migration/) for the full DB workflow and the
+See [Run a database migration](/data-persistence/how-to/database-migration/) for the full DB workflow and the
Aspire-up dependency.
## Step 4 — Configure the provider and secrets
@@ -354,7 +354,7 @@ aspire start first.
{{ comp.card({
title: "Run a database migration",
body: "The full db init / generate / seed / status workflow that applies auth.prisma.",
- href: "/how-to/database-migration/",
+ href: "/data-persistence/how-to/database-migration/",
icon: "+"
}) }}
diff --git a/docs/site/identity-access/how-to/index.md b/docs/site/identity-access/how-to/index.md
new file mode 100644
index 000000000..b561c099d
--- /dev/null
+++ b/docs/site/identity-access/how-to/index.md
@@ -0,0 +1,12 @@
+---
+layout: layouts/base.vto
+title: Recipes
+templateEngine: [vento, md]
+order: 100
+---
+
+# Identity & Access — recipes
+
+Task-oriented recipes for this area. Each one solves a single concrete problem and assumes you know
+the basics from the guides above it in the sidebar. The full cross-area catalog lives at
+[All how-to recipes]({{ "howto:index" |> xref |> url }}).
diff --git a/docs/site/identity-access/index.md b/docs/site/identity-access/index.md
index 064de5f6e..ffdaf2689 100644
--- a/docs/site/identity-access/index.md
+++ b/docs/site/identity-access/index.md
@@ -26,8 +26,17 @@ failure modes the contract closes off — is on the
{ eyebrow: "Capability", title: "Authentication", body: "The auth plugin story: one active backend, five endpoints, fail-loud typed boundaries, and the salt-gated audit surface.", href: "/identity-access/auth/", icon: "C" },
{ eyebrow: "Overview & Concepts", title: "Auth model", body: "Principal, session, backend, and authorization vocabulary.", href: "/explanation/auth-model/", icon: "O" },
{ eyebrow: "Quickstart", title: "Workspace auth", body: "Add authentication in the Workspace tutorial.", href: "/tutorials/workspace/02-auth/", icon: "Q" },
- { eyebrow: "How-To", title: "Add authentication", body: "Wire authentication into a generated workspace.", href: "/how-to/add-authentication/", icon: "H" },
+ { eyebrow: "How-To", title: "Add authentication", body: "Wire authentication into a generated workspace.", href: "/identity-access/how-to/add-authentication/", icon: "H" },
{ eyebrow: "How-To", title: "better-auth plugins", body: "Mount better-auth plugins through a typed passthrough; bearer and jwt run as-is, table-backed and interactive plugins carry caveats.", href: "/identity-access/better-auth-plugins/", icon: "H" },
{ eyebrow: "API Reference", title: "auth backends", body: "Generated symbols for auth, better-auth, kv-oauth, and WorkOS packages.", href: "/reference/auth/", icon: "R" },
{ eyebrow: "API Reference", title: "plugin auth", body: "Generated plugin-auth package symbols.", href: "/reference/plugin-auth/", icon: "R" }
] }) }}
+
+## Learn, do, look up
+
+{{ comp.cardsGrid({ columns: 4, cards: [
+ { eyebrow: "Learn", title: "Workspace tutorial", body: "Auth, workspace data, and route authorization in one track.", href: resolveXref("tut:workspace").href },
+ { eyebrow: "Do", title: "Recipes", body: "Task-oriented recipes for this area, one problem each.", href: "/identity-access/how-to/" },
+ { eyebrow: "Look up", title: "`@netscript/auth` reference", body: "Generated API reference. Related units: `auth-better-auth`, `auth-kv-oauth`, `auth-workos`, `plugin-auth`.", href: resolveXref("ref:auth").href },
+ { eyebrow: "Understand", title: "The auth model", body: "The design rationale behind this pillar.", href: resolveXref("explain:auth-model").href },
+] }) }}
diff --git a/docs/site/how-to/add-opentelemetry.md b/docs/site/observability/how-to/add-opentelemetry.md
similarity index 99%
rename from docs/site/how-to/add-opentelemetry.md
rename to docs/site/observability/how-to/add-opentelemetry.md
index 591123517..797aa9cf2 100644
--- a/docs/site/how-to/add-opentelemetry.md
+++ b/docs/site/observability/how-to/add-opentelemetry.md
@@ -2,8 +2,8 @@
layout: layouts/base.vto
title: Add OpenTelemetry
templateEngine: [vento, md]
-prev: { "label": "Queue / KV / cron", "href": "/how-to/queue-kv-cron/" }
-next: { "label": "Customize Fresh UI", "href": "/how-to/customize-fresh-ui/" }
+order: 101
+oldUrl: /how-to/add-opentelemetry/
---
# Add OpenTelemetry
diff --git a/docs/site/observability/how-to/index.md b/docs/site/observability/how-to/index.md
new file mode 100644
index 000000000..aef58c6de
--- /dev/null
+++ b/docs/site/observability/how-to/index.md
@@ -0,0 +1,12 @@
+---
+layout: layouts/base.vto
+title: Recipes
+templateEngine: [vento, md]
+order: 100
+---
+
+# Observability — recipes
+
+Task-oriented recipes for this area. Each one solves a single concrete problem and assumes you know
+the basics from the guides above it in the sidebar. The full cross-area catalog lives at
+[All how-to recipes]({{ "howto:index" |> xref |> url }}).
diff --git a/docs/site/observability/index.md b/docs/site/observability/index.md
index 0cfa7a4f8..a60907bf5 100644
--- a/docs/site/observability/index.md
+++ b/docs/site/observability/index.md
@@ -17,7 +17,7 @@ or connect package-level telemetry to the running system.
{{ comp.cardsGrid({ columns: 3, cards: [
{ eyebrow: "Overview & Concepts", title: "Telemetry model", body: "Trace context and logging across services, workers, sagas, and orchestration.", href: "/explanation/observability/", icon: "O" },
{ eyebrow: "Quickstart", title: "Turn on tracing", body: "Run aspire start, trigger a job, and watch the runtime's automatic spans land in the dashboard — the lowest-effort path to a first trace.", href: "/observability/telemetry/", icon: "Q" },
- { eyebrow: "How-To", title: "Add OpenTelemetry", body: "Wire OTel into a workspace.", href: "/how-to/add-opentelemetry/", icon: "H" },
+ { eyebrow: "How-To", title: "Add OpenTelemetry", body: "Wire OTel into a workspace.", href: "/observability/how-to/add-opentelemetry/", icon: "H" },
{ eyebrow: "API Reference", title: "telemetry", body: "Generated telemetry package symbols.", href: "/reference/telemetry/", icon: "R" },
{ eyebrow: "API Reference", title: "logger", body: "Generated logger package symbols.", href: "/reference/logger/", icon: "R" }
] }) }}
@@ -44,3 +44,12 @@ learn the model:
{{ comp.xref({ key: "ref:logger", text: "logger" }) }} references, and the
[telemetry convention](/reference/telemetry/convention/) (span naming, SpanKind, the `netscript.*`
attribute rules).
+
+## Learn, do, look up
+
+{{ comp.cardsGrid({ columns: 4, cards: [
+ { eyebrow: "Learn", title: "Trace the request model", body: "Follow one request through services, jobs, and streams in the core-concepts tour.", href: resolveXref("concept:concepts").href },
+ { eyebrow: "Do", title: "Recipes", body: "Task-oriented recipes for this area, one problem each.", href: "/observability/how-to/" },
+ { eyebrow: "Look up", title: "`@netscript/telemetry` reference", body: "Generated API reference. Related units: `logger`.", href: resolveXref("ref:telemetry").href },
+ { eyebrow: "Understand", title: "Observability", body: "The design rationale behind this pillar.", href: resolveXref("explain:observability").href },
+] }) }}
diff --git a/docs/site/observability/telemetry.md b/docs/site/observability/telemetry.md
index b97e95422..63b4b82dc 100644
--- a/docs/site/observability/telemetry.md
+++ b/docs/site/observability/telemetry.md
@@ -4,6 +4,7 @@ title: Telemetry & logging
templateEngine: [vento, md]
prev: { label: "KV, queues & cron", href: "/data-persistence/kv-queues-cron/" }
next: { label: "Fresh UI & design", href: "/web-layer/fresh-ui/" }
+order: 1
---
# Telemetry & logging
@@ -44,7 +45,7 @@ orchestrator — they are not separate processes you start by hand. Run
command (Aspire provisions Postgres and Redis first), then open the dashboard URL printed in
the console (https://localhost:18888, with a one-time auth token). Until Aspire is
running there is no :18888 surface to view traces or logs on. See
-Database & migration for the full startup order.
+Database & migration for the full startup order.
{{ /comp }}
## The story: one grouped trace, end to end
@@ -100,7 +101,7 @@ NetScript-owned keys under the single proprietary root `netscript.*` — correla
{
title: "Do — Add OpenTelemetry",
body: "Task-oriented recipe: lean on the automatic job spans, add custom spans via @netscript/telemetry withChildSpan, propagate traceparent across services and subprocesses, and point the OTLP export (:4318) at the dashboard.",
- href: "/how-to/add-opentelemetry/",
+ href: "/observability/how-to/add-opentelemetry/",
icon: "◆"
},
{
@@ -485,7 +486,7 @@ This hub is intentionally thin — the full generated API lives in the reference
{
title: "Do — Add OpenTelemetry",
body: "Task-oriented recipe: lean on the automatic job spans, add custom spans via @netscript/telemetry withChildSpan, propagate traceparent across services and subprocesses, and point the OTLP export (:4318) at the dashboard.",
- href: "/how-to/add-opentelemetry/",
+ href: "/observability/how-to/add-opentelemetry/",
icon: "◆"
},
{
diff --git a/docs/site/orchestration-runtime/cli-scaffold.md b/docs/site/orchestration-runtime/cli-scaffold.md
index 78ca4ea7e..ede4a6c47 100644
--- a/docs/site/orchestration-runtime/cli-scaffold.md
+++ b/docs/site/orchestration-runtime/cli-scaffold.md
@@ -4,6 +4,7 @@ title: CLI and Scaffold
templateEngine: [vento, md]
prev: { label: "Orchestration & Runtime", href: "/orchestration-runtime/" }
next: { label: "Runtime configuration", href: "/orchestration-runtime/runtime-config/" }
+order: 1
---
# CLI & scaffold
diff --git a/docs/site/how-to/add-a-plugin.md b/docs/site/orchestration-runtime/how-to/add-a-plugin.md
similarity index 97%
rename from docs/site/how-to/add-a-plugin.md
rename to docs/site/orchestration-runtime/how-to/add-a-plugin.md
index 579236345..d2bfb58a5 100644
--- a/docs/site/how-to/add-a-plugin.md
+++ b/docs/site/orchestration-runtime/how-to/add-a-plugin.md
@@ -2,12 +2,8 @@
layout: layouts/base.vto
title: Add a plugin
templateEngine: [vento, md]
-prev:
- label: How-to guides
- href: /how-to/
-next:
- label: Add a service
- href: /how-to/add-a-service/
+order: 106
+oldUrl: /how-to/add-a-plugin/
---
# Add a plugin
@@ -102,7 +98,7 @@ dependency, emits a user-owned auth/mod.ts glue barrel, registers t
auth-api service on port 8094, and contributes its package-provided
Prisma models. The active backend is selected at runtime with
NETSCRIPT_AUTH_BACKEND (default kv-oauth). See
-Configure authentication for the backend setup.
+Configure authentication for the backend setup.
{{ /comp }}
## Step 2 — Add the plugin
@@ -258,8 +254,8 @@ Run `netscript plugin --help` for the complete, version-accurate command set.
### Where to go next
- **Build on the plugin you just added.** Next up:
- [Add a service](/how-to/add-a-service/) to give the plugin something to call, or
- [Configure authentication](/how-to/add-authentication/) if you added the auth plugin.
+ [Add a service](/services-sdk/how-to/add-a-service/) to give the plugin something to call, or
+ [Configure authentication](/identity-access/how-to/add-authentication/) if you added the auth plugin.
- **Understand the model.** Read [Plugin architecture](/explanation/plugin-system/) for
the design behind installable capabilities, ports, and runtime registration.
- **Browse capabilities.** The [capabilities](/capabilities/) section maps each plugin to the
diff --git a/docs/site/how-to/author-a-plugin.md b/docs/site/orchestration-runtime/how-to/author-a-plugin.md
similarity index 98%
rename from docs/site/how-to/author-a-plugin.md
rename to docs/site/orchestration-runtime/how-to/author-a-plugin.md
index 020372b8f..d30422796 100644
--- a/docs/site/how-to/author-a-plugin.md
+++ b/docs/site/orchestration-runtime/how-to/author-a-plugin.md
@@ -2,8 +2,8 @@
layout: layouts/base.vto
title: Author a plugin
templateEngine: [vento, md]
-prev: { label: "Deploy to Deno Deploy", href: "/how-to/deploy-deno-deploy/" }
-next: { label: "Add authentication", href: "/how-to/add-authentication/" }
+order: 107
+oldUrl: /how-to/author-a-plugin/
---
# Author a plugin
@@ -11,7 +11,7 @@ next: { label: "Add authentication", href: "/how-to/add-authentication/" }
**Scope.** This recipe shows how to author a *new* custom plugin from scratch — its canonical
location, the manifest it exports through `definePlugin(...)`, the contribution shape the kernel
reads, and the generated registry that makes those contributions discoverable at runtime. It is the
-advanced companion to [Add a first-party plugin](/how-to/add-a-plugin/), which only *installs* one
+advanced companion to [Add a first-party plugin](/orchestration-runtime/how-to/add-a-plugin/), which only *installs* one
of the four official plugins (`workers`, `sagas`, `triggers`, `streams`). If you just want a worker
or a saga, install it there. Come here when you need a capability NetScript does not ship.
@@ -346,7 +346,7 @@ When your plugin grows beyond a single package — a core seam plus swappable ad
The pattern to copy: a **core package owns the port** (`AuthBackendPort`), each **adapter is pure**
(implements the port, declares no service), and the **plugin under `plugins/.withDependencies({...}) on the manifest and in
{{ comp.card({
title: "Add a first-party plugin",
body: "Install one of the four official plugins instead of writing your own.",
- href: "/how-to/add-a-plugin/",
+ href: "/orchestration-runtime/how-to/add-a-plugin/",
icon: "+"
}) }}
{{ comp.card({
title: "Add authentication",
body: "Build the multi-package auth plugin — the production exemplar for a core seam plus swappable backends.",
- href: "/how-to/add-authentication/",
+ href: "/identity-access/how-to/add-authentication/",
icon: "→"
}) }}
diff --git a/docs/site/how-to/deno-lsp-code-intelligence.md b/docs/site/orchestration-runtime/how-to/deno-lsp-code-intelligence.md
similarity index 97%
rename from docs/site/how-to/deno-lsp-code-intelligence.md
rename to docs/site/orchestration-runtime/how-to/deno-lsp-code-intelligence.md
index 9c2a4d189..5a8be798b 100644
--- a/docs/site/how-to/deno-lsp-code-intelligence.md
+++ b/docs/site/orchestration-runtime/how-to/deno-lsp-code-intelligence.md
@@ -2,8 +2,8 @@
layout: layouts/base.vto
title: Deno LSP code intelligence in Claude Code
templateEngine: [vento, md]
-prev: { label: "How-to guides", href: "/how-to/" }
-next: { label: "Deploy locally with Aspire", href: "/how-to/deploy-local-aspire/" }
+order: 108
+oldUrl: /how-to/deno-lsp-code-intelligence/
---
# Deno LSP code intelligence in Claude Code
diff --git a/docs/site/how-to/deploy-deno-deploy.md b/docs/site/orchestration-runtime/how-to/deploy-deno-deploy.md
similarity index 97%
rename from docs/site/how-to/deploy-deno-deploy.md
rename to docs/site/orchestration-runtime/how-to/deploy-deno-deploy.md
index 2cd7dc848..a228d1e0e 100644
--- a/docs/site/how-to/deploy-deno-deploy.md
+++ b/docs/site/orchestration-runtime/how-to/deploy-deno-deploy.md
@@ -2,8 +2,8 @@
layout: layouts/base.vto
title: Deploy to Deno Deploy
templateEngine: [vento, md]
-prev: { label: "Deploy", href: "/how-to/deploy/" }
-next: { label: "Author a plugin", href: "/how-to/author-a-plugin/" }
+order: 103
+oldUrl: /how-to/deploy-deno-deploy/
---
# Deploy to Deno Deploy
@@ -23,7 +23,7 @@ config-to-argv wiring, nothing more.
netscript deploy deno-deploy is fully wired and runnable. The Docker, Compose, and
Linux/systemd targets are config schema only — there is no runnable
netscript deploy docker|compose|linux verb yet. For those targets you assemble the
-deployment yourself from the primitives in the Deploy recipe.
+deployment yourself from the primitives in the Deploy recipe.
{{ /comp }}
## Before you start
@@ -185,7 +185,7 @@ Many NetScript runtime plugins (workers, sagas, triggers) require Deno KV and th
`--unstable-kv`. Those background processors are not a fit for a single Deno Deploy isolate — deploy a
KV-free entrypoint (for example a Fresh app or a stateless oRPC service) here, and run the KV-backed
processors on infrastructure that permits `--unstable-kv`, as covered in the
-[Deploy](/how-to/deploy/) recipe.
+[Deploy](/orchestration-runtime/how-to/deploy/) recipe.
## Troubleshooting
@@ -207,19 +207,19 @@ processors on infrastructure that permits `--unstable-kv`, as covered in the
{
title: "Deploy (containers & bare metal)",
body: "The manual path: deployable units from appsettings.json, backing services, per-process deno run commands, and the --no-aspire escape hatch.",
- href: "/how-to/deploy/",
+ href: "/orchestration-runtime/how-to/deploy/",
icon: "◆"
},
{
title: "Deploy locally with Aspire",
body: "Run the whole graph on one machine under the Aspire AppHost before you ship anything remote.",
- href: "/how-to/deploy-local-aspire/",
+ href: "/orchestration-runtime/how-to/deploy-local-aspire/",
icon: "◎"
},
{
title: "Add OpenTelemetry",
body: "Wire spans and traceparent propagation to your OTLP collector so a deployed process is observable.",
- href: "/how-to/add-opentelemetry/",
+ href: "/observability/how-to/add-opentelemetry/",
icon: "≋"
}
] }) }}
diff --git a/docs/site/how-to/deploy-local-aspire.md b/docs/site/orchestration-runtime/how-to/deploy-local-aspire.md
similarity index 97%
rename from docs/site/how-to/deploy-local-aspire.md
rename to docs/site/orchestration-runtime/how-to/deploy-local-aspire.md
index e0bda9505..6c90bfe8e 100644
--- a/docs/site/how-to/deploy-local-aspire.md
+++ b/docs/site/orchestration-runtime/how-to/deploy-local-aspire.md
@@ -2,8 +2,8 @@
layout: layouts/base.vto
title: Deploy locally with Aspire
templateEngine: [vento, md]
-prev: { label: "Graceful shutdown", href: "/how-to/graceful-shutdown/" }
-next: { label: "How-to guides", href: "/how-to/" }
+order: 102
+oldUrl: /how-to/deploy-local-aspire/
---
# Deploy locally with Aspire
@@ -11,7 +11,7 @@ next: { label: "How-to guides", href: "/how-to/" }
**Goal:** run your whole NetScript workspace on one machine under .NET Aspire — scaffold the
AppHost, bring up the resource graph (Postgres, Redis, every service and background processor),
and watch it from the Aspire dashboard. This is the **local** orchestration companion to the
-[Deploy](/how-to/deploy/) recipe (which covers shipping to a remote target); for *why* the
+[Deploy](/orchestration-runtime/how-to/deploy/) recipe (which covers shipping to a remote target); for *why* the
AppHost works the way it does, read [Orchestration with Aspire](/explanation/aspire/).
{{ comp callout { type: "important", title: "The order is: scaffold → orchestrate → database" } }}
@@ -198,4 +198,4 @@ a Deno workspace problem.
- **Exact symbols + full port map:** {{ comp.xref({ key: "ref:aspire", text: "the Aspire reference" }) }}
and the {{ comp.xref({ key: "cli:reference", text: "CLI reference" }) }}.
-{{ comp.nextPrev({ prev: { label: "Graceful shutdown", href: "/how-to/graceful-shutdown/" }, next: { label: "How-to guides", href: "/how-to/" } }) }}
+{{ comp.nextPrev({ prev: { label: "Graceful shutdown", href: "/orchestration-runtime/how-to/graceful-shutdown/" }, next: { label: "How-to guides", href: "/how-to/" } }) }}
diff --git a/docs/site/how-to/deploy.md b/docs/site/orchestration-runtime/how-to/deploy.md
similarity index 98%
rename from docs/site/how-to/deploy.md
rename to docs/site/orchestration-runtime/how-to/deploy.md
index 0ae0cc4c4..69d4b46e9 100644
--- a/docs/site/how-to/deploy.md
+++ b/docs/site/orchestration-runtime/how-to/deploy.md
@@ -2,8 +2,8 @@
layout: layouts/base.vto
title: Deploy
templateEngine: [vento, md]
-prev: { label: "Build a durable chat", href: "/how-to/build-a-durable-chat/" }
-next: { label: "Deploy to Deno Deploy", href: "/how-to/deploy-deno-deploy/" }
+order: 101
+oldUrl: /how-to/deploy/
---
# Deploy a NetScript workspace
@@ -51,7 +51,7 @@ The scaffolded workflows cover the deploy surfaces that ship today:
(netscript deploy build). They are intentionally starter pipelines: fill in repository
secrets, GitHub environment protection, host credentials, and target-specific configuration before
you treat them as production release jobs. See
-Deploy to Deno Deploy for the managed-platform command
+Deploy to Deno Deploy for the managed-platform command
reference.
{{ /comp }}
@@ -63,7 +63,7 @@ There is now a first-class, runnable managed-platform path:
preflight guard and deploy.targets['deno-deploy'] config. Aspire-backed targets are
also routed: netscript deploy docker|compose|kubernetes|azure-aca|azure-app-service|azure-aks|cloud-run <op>
delegates to the target adapter. See
-Deploy to Deno Deploy for Deno Deploy details.
+Deploy to Deno Deploy for Deno Deploy details.
{{ /comp }}
## Before you start
@@ -181,7 +181,7 @@ Postgres is provisioned by Aspire. So the order is always:
db seed. Running a DB command with no Postgres reachable — for example in an
isolated CI container — fails fast. In production, point the same commands at your managed
Postgres via POSTGRES_URI or DATABASE_URL instead of relying on
-Aspire. See Database migration for the full sequence.
+Aspire. See Database migration for the full sequence.
{{ /comp }}
## Step 3 — Provision backing services
@@ -419,7 +419,7 @@ deployment is live.
{
title: "Deploy to Deno Deploy",
body: "The one runnable managed-platform path: netscript deploy deno-deploy plan/up/status/logs/down, the unstable-API guard, and deploy.targets['deno-deploy'] config.",
- href: "/how-to/deploy-deno-deploy/",
+ href: "/orchestration-runtime/how-to/deploy-deno-deploy/",
icon: "◆"
},
{
@@ -431,13 +431,13 @@ deployment is live.
{
title: "Database migration",
body: "Run db init → generate → seed → status against Postgres — and why aspire start comes first.",
- href: "/how-to/database-migration/",
+ href: "/data-persistence/how-to/database-migration/",
icon: "▣"
},
{
title: "Add OpenTelemetry",
body: "Wire spans and traceparent propagation to your OTLP collector in production.",
- href: "/how-to/add-opentelemetry/",
+ href: "/observability/how-to/add-opentelemetry/",
icon: "≋"
}
] }) }}
diff --git a/docs/site/how-to/graceful-shutdown.md b/docs/site/orchestration-runtime/how-to/graceful-shutdown.md
similarity index 97%
rename from docs/site/how-to/graceful-shutdown.md
rename to docs/site/orchestration-runtime/how-to/graceful-shutdown.md
index 7fe144851..6f574d0a5 100644
--- a/docs/site/how-to/graceful-shutdown.md
+++ b/docs/site/orchestration-runtime/how-to/graceful-shutdown.md
@@ -2,8 +2,8 @@
layout: layouts/base.vto
title: Graceful shutdown
templateEngine: [vento, md]
-prev: { label: "Run a polyglot task", href: "/how-to/run-a-polyglot-task/" }
-next: { label: "Deploy locally with Aspire", href: "/how-to/deploy-local-aspire/" }
+order: 105
+oldUrl: /how-to/graceful-shutdown/
---
# Graceful shutdown
@@ -241,7 +241,7 @@ entrypoint by hand.
{
title: "Do — Tune the worker runtime",
body: "Recipe: concurrency, retry, and the runtime knobs that decide how much in-flight work a drain has to wait on.",
- href: "/how-to/tune-worker-runtime/",
+ href: "/background-processing/how-to/tune-worker-runtime/",
icon: "◆"
},
{
@@ -252,4 +252,4 @@ entrypoint by hand.
}
] }) }}
-{{ comp.nextPrev({ prev: { label: "Run a polyglot task", href: "/how-to/run-a-polyglot-task/" }, next: { label: "Deploy locally with Aspire", href: "/how-to/deploy-local-aspire/" } }) }}
+{{ comp.nextPrev({ prev: { label: "Run a polyglot task", href: "/background-processing/how-to/run-a-polyglot-task/" }, next: { label: "Deploy locally with Aspire", href: "/orchestration-runtime/how-to/deploy-local-aspire/" } }) }}
diff --git a/docs/site/orchestration-runtime/how-to/index.md b/docs/site/orchestration-runtime/how-to/index.md
new file mode 100644
index 000000000..4a5b79a08
--- /dev/null
+++ b/docs/site/orchestration-runtime/how-to/index.md
@@ -0,0 +1,12 @@
+---
+layout: layouts/base.vto
+title: Recipes
+templateEngine: [vento, md]
+order: 100
+---
+
+# Orchestration & Runtime — recipes
+
+Task-oriented recipes for this area. Each one solves a single concrete problem and assumes you know
+the basics from the guides above it in the sidebar. The full cross-area catalog lives at
+[All how-to recipes]({{ "howto:index" |> xref |> url }}).
diff --git a/docs/site/how-to/roll-out-runtime-overrides.md b/docs/site/orchestration-runtime/how-to/roll-out-runtime-overrides.md
similarity index 87%
rename from docs/site/how-to/roll-out-runtime-overrides.md
rename to docs/site/orchestration-runtime/how-to/roll-out-runtime-overrides.md
index f1c6fe960..9360ce937 100644
--- a/docs/site/how-to/roll-out-runtime-overrides.md
+++ b/docs/site/orchestration-runtime/how-to/roll-out-runtime-overrides.md
@@ -2,8 +2,8 @@
layout: layouts/base.vto
title: Roll out runtime overrides
templateEngine: [vento, md]
-prev: { label: "Deploy locally with Aspire", href: "/how-to/deploy-local-aspire/" }
-next: { label: "Add a task runtime adapter", href: "/how-to/add-a-task-runtime-adapter/" }
+order: 104
+oldUrl: /how-to/roll-out-runtime-overrides/
---
# Roll out runtime overrides
@@ -106,11 +106,11 @@ The next watcher reload sees the previous version and calls `onChange` again.
## Next steps
-- Tune worker behavior with [Tune the worker runtime](/how-to/tune-worker-runtime/).
-- Deploy the process with [Deploy](/how-to/deploy/).
+- Tune worker behavior with [Tune the worker runtime](/background-processing/how-to/tune-worker-runtime/).
+- Deploy the process with [Deploy](/orchestration-runtime/how-to/deploy/).
- Look up the package surface in [runtime-config reference](/reference/runtime-config/).
{{ comp.nextPrev({
- prev: { label: "Deploy locally with Aspire", href: "/how-to/deploy-local-aspire/" },
- next: { label: "Add a task runtime adapter", href: "/how-to/add-a-task-runtime-adapter/" }
+ prev: { label: "Deploy locally with Aspire", href: "/orchestration-runtime/how-to/deploy-local-aspire/" },
+ next: { label: "Add a task runtime adapter", href: "/background-processing/how-to/add-a-task-runtime-adapter/" }
}) }}
diff --git a/docs/site/orchestration-runtime/index.md b/docs/site/orchestration-runtime/index.md
index 50bd8af02..1dfc1bab2 100644
--- a/docs/site/orchestration-runtime/index.md
+++ b/docs/site/orchestration-runtime/index.md
@@ -26,9 +26,9 @@ workspace up, change runtime overrides, or understand how plugin contributions b
{ eyebrow: "Story", title: "CLI & scaffold", body: "One command to a complete workspace — and why generated conventions save agent turns.", href: "/orchestration-runtime/cli-scaffold/", icon: "S" },
{ eyebrow: "Story", title: "Runtime configuration", body: "Typed project config plus hot-reloadable operator overrides.", href: "/orchestration-runtime/runtime-config/", icon: "S" },
{ eyebrow: "Quickstart", title: "Run the workspace", body: "Scaffold and start a workspace from the quickstart.", href: "/quickstart/", icon: "Q" },
- { eyebrow: "How-To", title: "Deploy locally with Aspire", body: "Use Aspire to bring up local resources.", href: "/how-to/deploy-local-aspire/", icon: "H" },
- { eyebrow: "How-To", title: "Runtime overrides", body: "Roll out configuration overrides across resources.", href: "/how-to/roll-out-runtime-overrides/", icon: "H" },
- { eyebrow: "How-To", title: "Plugins", body: "Add or author a plugin contribution.", href: "/how-to/add-a-plugin/", icon: "H" },
+ { eyebrow: "How-To", title: "Deploy locally with Aspire", body: "Use Aspire to bring up local resources.", href: "/orchestration-runtime/how-to/deploy-local-aspire/", icon: "H" },
+ { eyebrow: "How-To", title: "Runtime overrides", body: "Roll out configuration overrides across resources.", href: "/orchestration-runtime/how-to/roll-out-runtime-overrides/", icon: "H" },
+ { eyebrow: "How-To", title: "Plugins", body: "Add or author a plugin contribution.", href: "/orchestration-runtime/how-to/add-a-plugin/", icon: "H" },
{ eyebrow: "API Reference", title: "aspire and runtime config", body: "Generated symbols for AppHost, config, runtime-config, plugin, and CLI units.", href: "/reference/aspire/", icon: "R" }
] }) }}
@@ -50,3 +50,12 @@ New here, start with the concept, then the practical wiring, then the reference:
dashboard side of the same graph — the spans and logs `aspire start` collects.
- **Look up exact symbols:** {{ comp.xref({ key: "ref:aspire", text: "the Aspire reference" }) }} and
the {{ comp.xref({ key: "cli:reference", text: "CLI reference" }) }}.
+
+## Learn, do, look up
+
+{{ comp.cardsGrid({ columns: 4, cards: [
+ { eyebrow: "Learn", title: "Quickstart", body: "From `netscript init` to a running, orchestrated workspace.", href: resolveXref("concept:quickstart").href },
+ { eyebrow: "Do", title: "Recipes", body: "Task-oriented recipes for this area, one problem each.", href: "/orchestration-runtime/how-to/" },
+ { eyebrow: "Look up", title: "`@netscript/aspire` reference", body: "Generated API reference. Related units: `cli`, `config`, `runtime-config`, `plugin`.", href: resolveXref("ref:aspire").href },
+ { eyebrow: "Understand", title: "Orchestration with Aspire", body: "The design rationale behind this pillar.", href: resolveXref("explain:aspire").href },
+] }) }}
diff --git a/docs/site/orchestration-runtime/runtime-config.md b/docs/site/orchestration-runtime/runtime-config.md
index b53e3bb1c..12e6ac060 100644
--- a/docs/site/orchestration-runtime/runtime-config.md
+++ b/docs/site/orchestration-runtime/runtime-config.md
@@ -4,6 +4,7 @@ title: Runtime configuration
templateEngine: [vento, md]
prev: { label: "Polyglot tasks", href: "/background-processing/polyglot-tasks/" }
next: { label: "Capabilities", href: "/capabilities/" }
+order: 2
---
# Runtime configuration
@@ -56,7 +57,7 @@ deliberately distinct: `@netscript/config` owns the build-time contract, and
{
title: "Do — Choose a queue provider",
body: "A config-driven decision recipe: pick and wire a queue backend through project config.",
- href: "/how-to/choose-a-queue-provider/",
+ href: "/data-persistence/how-to/choose-a-queue-provider/",
icon: "◆"
}
] }) }}
diff --git a/docs/site/quickstart.vto b/docs/site/quickstart.vto
index 47c006c37..524835fc3 100644
--- a/docs/site/quickstart.vto
+++ b/docs/site/quickstart.vto
@@ -104,7 +104,7 @@ service, and so on) — on the strict happy path above, the four steps are suffi
On the default path, Aspire is step 2 of running your app: cd aspire && aspire start
provisions Postgres and Redis first. Any netscript db command (init, generate, seed) runs
after Aspire is up, because it needs that database to exist. See
-Database migrations for the full flow.
+Database migrations for the full flow.
{{ /comp }}
## 3. Start it
diff --git a/docs/site/quickstart/aspire.md b/docs/site/quickstart/aspire.md
new file mode 100644
index 000000000..07c6d0997
--- /dev/null
+++ b/docs/site/quickstart/aspire.md
@@ -0,0 +1,85 @@
+---
+layout: layouts/base.vto
+title: "Aspire quickstart"
+templateEngine: [vento, md]
+---
+
+# Aspire quickstart
+
+A NetScript workspace is never one process — it is a Fresh app, oRPC services, plugin APIs,
+background processors, a database, and a cache. Aspire is how we make that whole fleet start with
+**one command**, wired together, with a real dashboard from the first run. This page is the
+shortest path to seeing it; the [main Quickstart](/quickstart/) covers installing the CLI and the
+scaffold in more detail.
+
+{{ comp callout { type: "important", title: "Alpha" } }}
+NetScript is alpha software and the API is subject to change. Pin versions in real projects.
+{{ /comp }}
+
+## What you get
+
+- **One workspace, one command up.** `aspire start` boots the database, the cache, every service,
+ every plugin API, and every background processor — in dependency order, no docker-compose to
+ babysit.
+- **Multi-resource wiring, resolved for you.** Connection strings and neighbour endpoints are
+ computed and injected as environment variables before each process starts, so nothing has to
+ discover anything at runtime.
+- **The Aspire dashboard.** Live resource list, per-process console logs, and distributed traces
+ in one place — `aspire start` prints its URL and a one-time login token.
+- **A TypeScript AppHost — not .NET authoring.** The orchestrator entry point is a generated
+ TypeScript program at `aspire/apphost.mts`, running on an isolated Node runtime inside
+ `aspire/` so it never leaks into your Deno workspace. You write no C#.
+
+{{ comp callout { type: "note", title: "Prerequisites" } }}
+Deno 2.x and the netscript CLI
+(install steps in the Quickstart), the external
+.NET Aspire CLI, and a
+running Docker daemon — Aspire provisions Postgres and Redis as local containers.
+{{ /comp }}
+
+## The commands
+
+Scaffold a workspace, then bring it up. The Aspire layer lives in its own `aspire/` folder;
+restore its SDK modules once, then start:
+
+```bash
+netscript init my-app --db postgres
+
+cd my-app/aspire
+aspire restore # one-time: downloads the AppHost SDK modules
+aspire start # boots Postgres + Redis + services, prints the dashboard URL
+```
+
+When boot settles, open the dashboard URL `aspire start` printed (conventionally
+`https://localhost:18888`) and paste the login token. Every resource, its logs, and its traces are
+one click away.
+
+{{ comp callout { type: "note", title: "Database commands come after" } }}
+netscript db init, db generate, and db seed run from the
+workspace root only once aspire start is up — they provision the
+database through the running AppHost. With no Aspire up there is no Postgres for them to
+reach.
+{{ /comp }}
+
+## Prefer no orchestration?
+
+Aspire is the default, not a requirement. Scaffold with `--no-aspire` to skip the orchestration
+layer entirely — no `aspire/` folder, no dashboard — and start the Fresh app directly:
+
+```bash
+netscript init my-app --db postgres --no-aspire
+deno task --cwd apps/dashboard dev
+```
+
+You take over infrastructure and wiring yourself: bring your own Postgres and cache, hand each
+process its connection strings. When that trade is the right call — and what exactly you give
+up — is covered in [Orchestration with Aspire]({{ "explain:aspire" |> xref |> url }}).
+
+## Where next
+
+- **Step-by-step recipe:** [Deploy locally with Aspire]({{ "howto:deploy-local-aspire" |> xref |> url }}) —
+ the full local flow, including the database sequence and first-run footguns.
+- **Why it works this way:** [Orchestration with Aspire]({{ "explain:aspire" |> xref |> url }}) —
+ the AppHost, plugin contributions, and the resource graph.
+- **Exact symbols and the port map:** [the Aspire reference]({{ "ref:aspire" |> xref |> url }}) and
+ the [CLI reference]({{ "cli:reference" |> xref |> url }}).
diff --git a/docs/site/reference/ai/skills.md b/docs/site/reference/ai/skills.md
index 6e0c34776..dbce3d327 100644
--- a/docs/site/reference/ai/skills.md
+++ b/docs/site/reference/ai/skills.md
@@ -26,7 +26,7 @@ of three ready-made skills that teach an agent the NetScript vocabulary.
`netscript agent init` installs three first-party skills on the **Claude Code host path**
(under `.claude/skills/`, alongside `.mcp.json` and the marked `AGENTS.md` section); the
VS Code host path writes `.vscode/mcp.json` only, with no skills. They share one vocabulary with the
-`netscript` CLI and the [NetScript MCP tools](/capabilities/agent-tooling/#tool-catalog),
+`netscript` CLI and the [NetScript MCP tools](/ai/agent-tooling/#tool-catalog),
so an agent routes to a workflow and then reaches for the matching CLI verb or MCP tool.
| Skill | Role |
@@ -37,7 +37,7 @@ so an agent routes to a workflow and then reaches for the matching CLI verb or M
These are the skills named in the bundle manifest and installed as an atomic set;
re-running `netscript agent init` is idempotent. See
-[Agent tooling](/capabilities/agent-tooling/) for the CLI × skills × MCP combo.
+[Agent tooling](/ai/agent-tooling/) for the CLI × skills × MCP combo.
```ts
import {
diff --git a/docs/site/reference/cli/commands.md b/docs/site/reference/cli/commands.md
index 70831baef..cb6765db6 100644
--- a/docs/site/reference/cli/commands.md
+++ b/docs/site/reference/cli/commands.md
@@ -49,7 +49,7 @@ the [quickstart](/quickstart/); every flag is:
## `agent` — install and run agent tooling
`netscript agent` installs and runs the shared CLI × skills × MCP tooling. See
-[Agent tooling](/capabilities/agent-tooling/) for the mental model and the
+[Agent tooling](/ai/agent-tooling/) for the mental model and the
[`@netscript/mcp` reference](/reference/mcp/) for the server's tool surface.
| Command | Description |
@@ -134,7 +134,7 @@ the [CLI reference](/cli-reference/#plugins). The full group also carries:
| `netscript plugin auth session revoke 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.
+Deploy locally with Aspire.
{{ /comp }}
## Verify your progress
diff --git a/docs/site/tutorials/erp-sync/03-polyglot-transform.md b/docs/site/tutorials/erp-sync/03-polyglot-transform.md
index 9a83b680d..d4d12dff7 100644
--- a/docs/site/tutorials/erp-sync/03-polyglot-transform.md
+++ b/docs/site/tutorials/erp-sync/03-polyglot-transform.md
@@ -147,7 +147,7 @@ Calling .build() on a deno task without
Always pass an explicit, least-privilege set. The framework also ships named
permissions presets (minimal, readOnly,
network, …) so you do not hand-roll the object for common cases — see
-Tune the worker runtime.
+Tune the worker runtime.
{{ /comp }}
## Step 3 — Run it through the executor
@@ -261,7 +261,7 @@ A missing interpreter surfaces as a failed task, not a thrown error: ex
127 is reported as command not found and 126 as command not
executable. Confirm python3 --version / pwsh --version /
dotnet --version on the actual worker host before you ship a task that depends on it.
-When yours is ready, Run a polyglot task walks the
+When yours is ready, Run a polyglot task walks the
Python and shell variants end to end, including interpreter pinning.
{{ /comp }}
@@ -312,11 +312,11 @@ on a schedule.
## Where to go deeper
-- **Run the Python/shell variants for real** → [Run a polyglot task](/how-to/run-a-polyglot-task/)
+- **Run the Python/shell variants for real** → [Run a polyglot task](/background-processing/how-to/run-a-polyglot-task/)
— the hands-on recipe: define, write the script, pin the interpreter, read the result.
- **The capability** → [Polyglot tasks](/background-processing/polyglot-tasks/) — the WHY: what a
task is, the subprocess seam, the full `TaskResult` shape.
-- **Tune the runtime** → [Tune the worker runtime](/how-to/tune-worker-runtime/) — concurrency,
+- **Tune the runtime** → [Tune the worker runtime](/background-processing/how-to/tune-worker-runtime/) — concurrency,
the permission presets, and the per-task timeout/retry knobs.
{{ comp.nextPrev({ prev: { label: "2 · Import job", href: "/tutorials/erp-sync/02-import-job/" }, next: { label: "4 · Queue & cron", href: "/tutorials/erp-sync/04-queue-and-cron/" } }) }}
diff --git a/docs/site/tutorials/erp-sync/04-queue-and-cron.md b/docs/site/tutorials/erp-sync/04-queue-and-cron.md
index 572df1ad1..95b6dee1f 100644
--- a/docs/site/tutorials/erp-sync/04-queue-and-cron.md
+++ b/docs/site/tutorials/erp-sync/04-queue-and-cron.md
@@ -67,7 +67,7 @@ Auto-discovery probes RabbitMQ → Redis → Deno KV only. It w
fall through to PostgreSQL, even when a Postgres connection is present. The SQL-durable queue is
opt-in — you must pass provider: QueueProvider.Postgres or you will quietly land on the
Deno KV adapter. The full decision guide is in
-Choose a queue provider.
+Choose a queue provider.
{{ /comp }}
## Step 2 — Size worker concurrency in config
@@ -105,7 +105,7 @@ export const workers = defineWorkers({
For per-topic control — a hot `imports` topic at concurrency 10 while a heavy `reports` topic stays
at 1 — use a `WorkerGroup` with its own `scaling: { mode, concurrency }`. The full per-topic and
-runner-mode knobs are in [Tune the worker runtime](/how-to/tune-worker-runtime/).
+runner-mode knobs are in [Tune the worker runtime](/background-processing/how-to/tune-worker-runtime/).
{{ comp callout { type: "warning", title: "Set scaling.concurrency in config — the Aspire env var is silently ignored" } }}
There are two concurrency env names in play and they are not the same
@@ -229,7 +229,7 @@ schedule, not a file event, enqueued it. Set the cron back to `0 6 * * *` when y
Raising concurrency means more jobs run at once, and every queue backend can redeliver a
message on retry. An idempotent import — keyed on the file's content hash or name — can re-run without
double-importing. Pair retries with an idempotencyKey on enqueue so a redelivery after a
-restart does not duplicate rows. See Choose a queue
+restart does not duplicate rows. See Choose a queue
provider for each backend's delivery semantics.
{{ /comp }}
diff --git a/docs/site/tutorials/erp-sync/05-deploy.md b/docs/site/tutorials/erp-sync/05-deploy.md
index 07579c833..aa99f4e1c 100644
--- a/docs/site/tutorials/erp-sync/05-deploy.md
+++ b/docs/site/tutorials/erp-sync/05-deploy.md
@@ -41,8 +41,8 @@ stitch a file drop to its job execution.
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. Shipping to a remote target (managed infrastructure, your own process lifecycle) is the
-Deploy recipe; this chapter is the local companion. The full local
-walkthrough lives in Deploy locally with Aspire.
+Deploy recipe; this chapter is the local companion. The full local
+walkthrough lives in Deploy locally with Aspire.
{{ /comp }}
## Before you begin
@@ -212,12 +212,12 @@ and you know exactly where the local story ends and a production deployment begi
You have finished the ERP Sync track. From here, branch into task-oriented and reference docs:
-- **Ship it remotely** → [Deploy](/how-to/deploy/) — the production companion to this local run:
+- **Ship it remotely** → [Deploy](/orchestration-runtime/how-to/deploy/) — the production companion to this local run:
deployable units, managed backing services, and the `--no-aspire` path.
-- **Take the transform polyglot** → [Run a polyglot task](/how-to/run-a-polyglot-task/) — swap
+- **Take the transform polyglot** → [Run a polyglot task](/background-processing/how-to/run-a-polyglot-task/) — swap
Chapter 3's Deno transform for a Python or shell step on your own host.
-- **Tune throughput** → [Choose a queue provider](/how-to/choose-a-queue-provider/) and
- [Tune the worker runtime](/how-to/tune-worker-runtime/).
+- **Tune throughput** → [Choose a queue provider](/data-persistence/how-to/choose-a-queue-provider/) and
+ [Tune the worker runtime](/background-processing/how-to/tune-worker-runtime/).
- **Understand the orchestrator** → [Orchestration with Aspire](/explanation/aspire/) and the full
[How-to guides](/how-to/).
diff --git a/docs/site/tutorials/erp-sync/index.md b/docs/site/tutorials/erp-sync/index.md
index 29b79aa81..4a5835b22 100644
--- a/docs/site/tutorials/erp-sync/index.md
+++ b/docs/site/tutorials/erp-sync/index.md
@@ -4,6 +4,7 @@ title: ERP Sync
templateEngine: [vento, md]
prev: { label: "Tutorials", href: "/tutorials/" }
next: { label: "1 · Scaffold", href: "/tutorials/erp-sync/01-scaffold/" }
+order: 5
---
# ERP Sync
@@ -122,3 +123,5 @@ instead of resetting between chapters. Follow them in order.
{{ /comp }}
{{ comp.nextPrev({ prev: { label: "Tutorials", href: "/tutorials/" }, next: { label: "1 · Scaffold", href: "/tutorials/erp-sync/01-scaffold/" } }) }}
+
+After this track, keep building in **Build › [Background jobs](/background-processing/)** — the guides and recipes there pick up where these chapters stop.
diff --git a/docs/site/tutorials/live-dashboard/02-contract-to-service.md b/docs/site/tutorials/live-dashboard/02-contract-to-service.md
index b7f95f9d0..4f45746fd 100644
--- a/docs/site/tutorials/live-dashboard/02-contract-to-service.md
+++ b/docs/site/tutorials/live-dashboard/02-contract-to-service.md
@@ -208,7 +208,7 @@ netscript db seed # seed development data (orders, users, products
These talk to the Postgres container Aspire provisioned. Run them with no Aspire up and they fail —
there is no database to reach. The full sequence is in
-[Database & migration](/how-to/database-migration/).
+[Database & migration](/data-persistence/how-to/database-migration/).
## Verify your progress
diff --git a/docs/site/tutorials/live-dashboard/03-sdk-cache-first-query.md b/docs/site/tutorials/live-dashboard/03-sdk-cache-first-query.md
index ebb4f7e2f..f381f5ea0 100644
--- a/docs/site/tutorials/live-dashboard/03-sdk-cache-first-query.md
+++ b/docs/site/tutorials/live-dashboard/03-sdk-cache-first-query.md
@@ -66,7 +66,7 @@ export const ordersClient = createServiceClienthttp://localhost:3002. serviceName: 'orders' is resolved at call time from an Aspire-injected env var — server-side services__orders__http__0, and the browser mirror VITE_services__orders__http__0 — via getServiceUrl in @netscript/sdk/discovery. Aspire sets those when you list orders as a reference; the client just reads them. Full mechanics in Discover services.
+You never hardcode http://localhost:3002. serviceName: 'orders' is resolved at call time from an Aspire-injected env var — server-side services__orders__http__0, and the browser mirror VITE_services__orders__http__0 — via getServiceUrl in @netscript/sdk/discovery. Aspire sets those when you list orders as a reference; the client just reads them. Full mechanics in Discover services.
{{ /comp }}
## Step 2 — Add the cache-first query factory
diff --git a/docs/site/tutorials/live-dashboard/06-deploy.md b/docs/site/tutorials/live-dashboard/06-deploy.md
index 6b4854848..815552572 100644
--- a/docs/site/tutorials/live-dashboard/06-deploy.md
+++ b/docs/site/tutorials/live-dashboard/06-deploy.md
@@ -148,7 +148,7 @@ Both should return healthy responses, and the dashboard at `:18888` should list
- [ ] Creating an order is visible in the dashboard traces and the live monitor.
{{ comp callout { type: "warning", title: "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.
+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.
{{ /comp }}
{{ comp callout { type: "warning", title: "Footguns when aspire start will not boot" } }}
@@ -172,7 +172,7 @@ NetScript spine.
- **Task recipes** → the [how-to guides](/how-to/) cover what the tutorials don't: adding plugins,
database migrations, queue backends, and production pitfalls.
-- **Ship it remotely** → [Deploy](/how-to/deploy/) is the production companion to local Aspire.
+- **Ship it remotely** → [Deploy](/orchestration-runtime/how-to/deploy/) is the production companion to local Aspire.
- **Go deeper** → [Orchestration with Aspire](/explanation/aspire/) explains the AppHost, plugin
contributions, and two-pass reference resolution; {{ comp.xref({ key: "cap:streams" }) }} and
{{ comp.xref({ key: "cap:fresh-framework", text: "the Fresh meta-framework" }) }} back chapters 4
diff --git a/docs/site/tutorials/live-dashboard/index.md b/docs/site/tutorials/live-dashboard/index.md
index f819d3cb8..1732bc010 100644
--- a/docs/site/tutorials/live-dashboard/index.md
+++ b/docs/site/tutorials/live-dashboard/index.md
@@ -4,6 +4,7 @@ title: Live Dashboard
templateEngine: [vento, md]
prev: { label: "Tutorials", href: "/tutorials/" }
next: { label: "1 · Scaffold", href: "/tutorials/live-dashboard/01-scaffold/" }
+order: 1
---
# Live Dashboard
@@ -111,3 +112,5 @@ contract to durable stream. Every hop is typed off the same contract, so the row
the row the service wrote.
{{ comp.nextPrev({ prev: { label: "Tutorials", href: "/tutorials/" }, next: { label: "1 · Scaffold", href: "/tutorials/live-dashboard/01-scaffold/" } }) }}
+
+After this track, keep building in **Build › [Web Layer](/web-layer/)** — the guides and recipes there pick up where these chapters stop.
diff --git a/docs/site/tutorials/storefront/03-cart-contracts.md b/docs/site/tutorials/storefront/03-cart-contracts.md
index 14525c50e..ea16bf540 100644
--- a/docs/site/tutorials/storefront/03-cart-contracts.md
+++ b/docs/site/tutorials/storefront/03-cart-contracts.md
@@ -57,7 +57,7 @@ it.
## Step 1 — Scaffold the cart contract
Let the CLI lay down the contract file and wire it into the version aggregate for you, exactly as the
-[Add a service](/how-to/add-a-service/) recipe does. From the workspace root:
+[Add a service](/services-sdk/how-to/add-a-service/) recipe does. From the workspace root:
```sh
netscript contract add cart
diff --git a/docs/site/tutorials/storefront/06-storefront-ui.md b/docs/site/tutorials/storefront/06-storefront-ui.md
index 663eb4151..2103f136d 100644
--- a/docs/site/tutorials/storefront/06-storefront-ui.md
+++ b/docs/site/tutorials/storefront/06-storefront-ui.md
@@ -156,7 +156,7 @@ You never write http://localhost:3001. serviceName: 'products
call time from an Aspire-injected env var — server-side services__products__http__0 and
its browser mirror — so the same client works in a page loader and in a hydrated island. Aspire sets
those when the app lists products as a reference; the client just reads them. Full
-mechanics in Discover services.
+mechanics in Discover services.
{{ /comp }}
## Step 3 — Read and mutate in the island
diff --git a/docs/site/tutorials/storefront/07-deploy.md b/docs/site/tutorials/storefront/07-deploy.md
index d92b71ccb..17452a9ad 100644
--- a/docs/site/tutorials/storefront/07-deploy.md
+++ b/docs/site/tutorials/storefront/07-deploy.md
@@ -39,7 +39,7 @@ infrastructure, services, plugin APIs, and background processors — then gives
whole thing. You will read the live port map from the dashboard and confirm every resource is healthy.
{{ comp callout { type: "warning", title: "This is the LOCAL story — not a production deployer" } }}
-aspire start exists to make one command produce a complete, 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. NetScript does not ship a cloud deployer for your app; for a remote target you point processes at managed infrastructure yourself. This chapter teaches the local topology. See the Deploy and Deploy locally with Aspire how-to guides for the production-vs-local split.
+aspire start exists to make one command produce a complete, 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. NetScript does not ship a cloud deployer for your app; for a remote target you point processes at managed infrastructure yourself. This chapter teaches the local topology. See the Deploy and Deploy locally with Aspire how-to guides for the production-vs-local split.
{{ /comp }}
## Before you begin
@@ -185,9 +185,9 @@ where it counts, and verified at its edges.
## Where to go next
-- **Ship it somewhere real** → the [Deploy](/how-to/deploy/) how-to (production targets) and
- [Deploy locally with Aspire](/how-to/deploy-local-aspire/) (the full local recipe with every flag).
-- **Add observability** → [Add OpenTelemetry](/how-to/add-opentelemetry/) and the
+- **Ship it somewhere real** → the [Deploy](/orchestration-runtime/how-to/deploy/) how-to (production targets) and
+ [Deploy locally with Aspire](/orchestration-runtime/how-to/deploy-local-aspire/) (the full local recipe with every flag).
+- **Add observability** → [Add OpenTelemetry](/observability/how-to/add-opentelemetry/) and the
[Observability explanation](/explanation/observability/).
- **Go deeper on the ideas** → [Durability model](/explanation/durability-model/),
[Contracts & type flow](/explanation/contracts/), and [The plugin system](/explanation/plugin-system/).
diff --git a/docs/site/tutorials/storefront/index.md b/docs/site/tutorials/storefront/index.md
index 98895c2d9..81d1c3ff4 100644
--- a/docs/site/tutorials/storefront/index.md
+++ b/docs/site/tutorials/storefront/index.md
@@ -4,6 +4,7 @@ title: Build a storefront backend
templateEngine: [vento, md]
prev: { label: "Tutorials", href: "/tutorials/" }
next: { label: "1 · Scaffold", href: "/tutorials/storefront/01-scaffold/" }
+order: 4
---
# Build a storefront backend
@@ -121,3 +122,5 @@ This is a tutorial track: state compounds. The my-shop/ workspace y
## Start the build
{{ comp.nextPrev({ prev: { label: "Tutorials", href: "/tutorials/" }, next: { label: "1 · Scaffold", href: "/tutorials/storefront/01-scaffold/" } }) }}
+
+After this track, keep building in **Build › [Services & SDK](/services-sdk/)** — the guides and recipes there pick up where these chapters stop.
diff --git a/docs/site/tutorials/workspace/06-deploy.md b/docs/site/tutorials/workspace/06-deploy.md
index 502997dce..6d1915759 100644
--- a/docs/site/tutorials/workspace/06-deploy.md
+++ b/docs/site/tutorials/workspace/06-deploy.md
@@ -152,7 +152,7 @@ observable, correctly-wired stack on one machine. The Postgres
are throwaway Docker containers for dev convenience — not your production database or
cache, and the kv-oauth session store and auth credentials here are local-dev values. For
a remote target you point processes at managed infrastructure and let your platform own lifecycle;
-that is the Deploy recipe.
+that is the Deploy recipe.
{{ /comp }}
{{ comp callout { type: "warning", title: "Footguns when aspire start will not boot" } }}
@@ -181,7 +181,7 @@ gets a `401`; the engineer you paged gets provisioned without anyone waiting on
## Where to go next
-- **Ship it remotely** → [Deploy](/how-to/deploy/) — the production companion: deployable units,
+- **Ship it remotely** → [Deploy](/orchestration-runtime/how-to/deploy/) — the production companion: deployable units,
managed backing services, and the `--no-aspire` path.
- **Go deeper on auth** → [Authentication capability](/capabilities/auth/) and
[The authentication model](/explanation/auth-model/).
diff --git a/docs/site/tutorials/workspace/index.md b/docs/site/tutorials/workspace/index.md
index ff750576f..e7a6a2def 100644
--- a/docs/site/tutorials/workspace/index.md
+++ b/docs/site/tutorials/workspace/index.md
@@ -4,6 +4,7 @@ title: Team Workspace
templateEngine: [vento, md]
prev: { label: "Tutorials", href: "/tutorials/" }
next: { label: "1 · Scaffold", href: "/tutorials/workspace/01-scaffold/" }
+order: 3
---
# Team Workspace
@@ -134,3 +135,5 @@ chapter 1 and keep the same `my-workspace/` through to deploy.
{{ comp.nextPrev({ prev: { label: "Tutorials", href: "/tutorials/" }, next: { label: "1 · Scaffold", href: "/tutorials/workspace/01-scaffold/" } }) }}
+
+After this track, keep building in **Build › [Identity & Access](/identity-access/)** — the guides and recipes there pick up where these chapters stop.
diff --git a/docs/site/web-layer/builders.md b/docs/site/web-layer/builders.md
index 94d8519f8..b36138070 100644
--- a/docs/site/web-layer/builders.md
+++ b/docs/site/web-layer/builders.md
@@ -2,6 +2,7 @@
layout: layouts/base.vto
title: Pages and the define-page builder
templateEngine: [vento, md]
+order: 2
---
# Pages and the define-page builder
diff --git a/docs/site/web-layer/defer-streaming-ui.md b/docs/site/web-layer/defer-streaming-ui.md
index e1204c9cc..3c136b03a 100644
--- a/docs/site/web-layer/defer-streaming-ui.md
+++ b/docs/site/web-layer/defer-streaming-ui.md
@@ -2,6 +2,7 @@
layout: layouts/base.vto
title: Deferred and streaming UI
templateEngine: [vento, md]
+order: 6
---
# Deferred and streaming UI
diff --git a/docs/site/web-layer/error.md b/docs/site/web-layer/error.md
index 76ea988c1..f86c410e2 100644
--- a/docs/site/web-layer/error.md
+++ b/docs/site/web-layer/error.md
@@ -2,6 +2,7 @@
layout: layouts/base.vto
title: Error handling and diagnostics
templateEngine: [vento, md]
+order: 9
---
# Error handling and diagnostics
diff --git a/docs/site/web-layer/examples.md b/docs/site/web-layer/examples.md
index 6e3d13b00..69767b391 100644
--- a/docs/site/web-layer/examples.md
+++ b/docs/site/web-layer/examples.md
@@ -2,6 +2,7 @@
layout: layouts/base.vto
title: Examples and sandbox
templateEngine: [vento, md]
+order: 11
---
# Examples and sandbox
diff --git a/docs/site/web-layer/form.md b/docs/site/web-layer/form.md
index 523fed433..968cdbd53 100644
--- a/docs/site/web-layer/form.md
+++ b/docs/site/web-layer/form.md
@@ -2,6 +2,7 @@
layout: layouts/base.vto
title: Server-validated forms
templateEngine: [vento, md]
+order: 5
---
# Server-validated forms
diff --git a/docs/site/web-layer/fresh-ui.md b/docs/site/web-layer/fresh-ui.md
index 73f15cada..35b86d551 100644
--- a/docs/site/web-layer/fresh-ui.md
+++ b/docs/site/web-layer/fresh-ui.md
@@ -4,6 +4,7 @@ title: Fresh UI & design
templateEngine: [vento, md]
prev: { label: "Telemetry & logging", href: "/observability/telemetry/" }
next: { label: "Authentication", href: "/identity-access/auth/" }
+order: 12
---
# Fresh UI & design
@@ -47,7 +48,7 @@ drift from your oRPC contracts. Use @netscript/freshapps/dashboard
(the scaffolded app) as the worked reference to copy patterns from. For the data boundary the UI
renders see Services & contracts; to add or regenerate UI
-pieces see Customize the Fresh UI.
+pieces see Customize the Fresh UI.
{{ /comp }}
## Two layers, one design grammar
@@ -287,7 +288,7 @@ matches what you're doing.
{
title: "Do — Customize the Fresh UI",
body: "Task recipe: ui:init / ui:add, the copy-source ownership model, and editing the design tokens.",
- href: "/how-to/customize-fresh-ui/",
+ href: "/web-layer/how-to/customize-fresh-ui/",
icon: "◆"
},
{
diff --git a/docs/site/how-to/build-a-desktop-frontend.md b/docs/site/web-layer/how-to/build-a-desktop-frontend.md
similarity index 96%
rename from docs/site/how-to/build-a-desktop-frontend.md
rename to docs/site/web-layer/how-to/build-a-desktop-frontend.md
index 0115e120a..065f269db 100644
--- a/docs/site/how-to/build-a-desktop-frontend.md
+++ b/docs/site/web-layer/how-to/build-a-desktop-frontend.md
@@ -2,8 +2,8 @@
layout: layouts/base.vto
title: Building a desktop frontend the NetScript way
templateEngine: [vento, md]
-prev: { 'label': 'Customize Fresh UI', 'href': '/how-to/customize-fresh-ui/' }
-next: { 'label': 'Build a server-validated form', 'href': '/how-to/build-a-server-validated-form/' }
+order: 102
+oldUrl: /how-to/build-a-desktop-frontend/
---
# Building a desktop frontend the NetScript way
@@ -153,7 +153,7 @@ packaged-runtime check. {{ /comp }}
## See also
-- [Customize Fresh UI](/how-to/customize-fresh-ui/)
+- [Customize Fresh UI](/web-layer/how-to/customize-fresh-ui/)
- [`@netscript/fresh` reference](/reference/fresh/)
- [`@netscript/sdk` reference](/reference/sdk/)
- [`@netscript/fresh-ui` reference](/reference/fresh-ui/)
diff --git a/docs/site/how-to/build-a-server-validated-form.md b/docs/site/web-layer/how-to/build-a-server-validated-form.md
similarity index 88%
rename from docs/site/how-to/build-a-server-validated-form.md
rename to docs/site/web-layer/how-to/build-a-server-validated-form.md
index 1ca63294d..b0e32ea6f 100644
--- a/docs/site/how-to/build-a-server-validated-form.md
+++ b/docs/site/web-layer/how-to/build-a-server-validated-form.md
@@ -2,8 +2,8 @@
layout: layouts/base.vto
title: Build a server-validated form
templateEngine: [vento, md]
-prev: { label: "Add a task runtime adapter", href: "/how-to/add-a-task-runtime-adapter/" }
-next: { label: "Build a validated ingestion queue", href: "/how-to/build-a-validated-ingestion-queue/" }
+order: 103
+oldUrl: /how-to/build-a-server-validated-form/
---
# Build a server-validated form
@@ -102,6 +102,6 @@ Deno.test('contact page registers the route pattern', () => {
- Look up the builder surface in [fresh reference](/reference/fresh/).
{{ comp.nextPrev({
- prev: { label: "Add a task runtime adapter", href: "/how-to/add-a-task-runtime-adapter/" },
- next: { label: "Build a validated ingestion queue", href: "/how-to/build-a-validated-ingestion-queue/" }
+ prev: { label: "Add a task runtime adapter", href: "/background-processing/how-to/add-a-task-runtime-adapter/" },
+ next: { label: "Build a validated ingestion queue", href: "/durable-workflows/how-to/build-a-validated-ingestion-queue/" }
}) }}
diff --git a/docs/site/how-to/customize-fresh-ui.md b/docs/site/web-layer/how-to/customize-fresh-ui.md
similarity index 97%
rename from docs/site/how-to/customize-fresh-ui.md
rename to docs/site/web-layer/how-to/customize-fresh-ui.md
index 08a325adc..8be2b2a0c 100644
--- a/docs/site/how-to/customize-fresh-ui.md
+++ b/docs/site/web-layer/how-to/customize-fresh-ui.md
@@ -2,8 +2,8 @@
layout: layouts/base.vto
title: Customize Fresh UI
templateEngine: [vento, md]
-prev: { "label": "Add OpenTelemetry", "href": "/how-to/add-opentelemetry/" }
-next: { "label": "Build a durable chat", "href": "/how-to/build-a-durable-chat/" }
+order: 101
+oldUrl: /how-to/customize-fresh-ui/
---
# Customize Fresh UI
@@ -204,7 +204,7 @@ When an island or route needs server data, prefer the runtime's typed query laye
(@netscript/fresh/query) over hand-rolled fetch calls — it
carries the oRPC contract types through to the client so a renamed service field
surfaces as a type error. See @netscript/fresh
-for the query builders, and Add a service for
+for the query builders, and Add a service for
the backend half of that contract.
{{ /comp }}
@@ -389,9 +389,9 @@ type.
[`@netscript/fresh`](/reference/fresh/). These are the authority for every export
(the `/server`, `/query`, and sibling subpaths included); this guide never
duplicates them.
-- Related recipes: [Add a service](/how-to/add-a-service/) to give your UI a typed
- oRPC backend, and [Add OpenTelemetry](/how-to/add-opentelemetry/) to trace it.
+- Related recipes: [Add a service](/services-sdk/how-to/add-a-service/) to give your UI a typed
+ oRPC backend, and [Add OpenTelemetry](/observability/how-to/add-opentelemetry/) to trace it.
- Concepts: the [contracts](/explanation/contracts/) explanation shows how a typed
contract flows from service to client to island.
-{{ comp.nextPrev({ prev: { label: "Add OpenTelemetry", href: "/how-to/add-opentelemetry/" }, next: { label: "Build a durable chat", href: "/how-to/build-a-durable-chat/" } }) }}
+{{ comp.nextPrev({ prev: { label: "Add OpenTelemetry", href: "/observability/how-to/add-opentelemetry/" }, next: { label: "Build a durable chat", href: "/ai/how-to/build-a-durable-chat/" } }) }}
diff --git a/docs/site/web-layer/how-to/index.md b/docs/site/web-layer/how-to/index.md
new file mode 100644
index 000000000..67c4b251c
--- /dev/null
+++ b/docs/site/web-layer/how-to/index.md
@@ -0,0 +1,12 @@
+---
+layout: layouts/base.vto
+title: Recipes
+templateEngine: [vento, md]
+order: 100
+---
+
+# Web Layer — recipes
+
+Task-oriented recipes for this area. Each one solves a single concrete problem and assumes you know
+the basics from the guides above it in the sidebar. The full cross-area catalog lives at
+[All how-to recipes]({{ "howto:index" |> xref |> url }}).
diff --git a/docs/site/web-layer/index.md b/docs/site/web-layer/index.md
index a7a1b03af..51414ad50 100644
--- a/docs/site/web-layer/index.md
+++ b/docs/site/web-layer/index.md
@@ -72,8 +72,17 @@ scaffolded dashboard app — is [Fresh UI & design](/web-layer/fresh-ui/), which
{ eyebrow: "Overview & Concepts", title: "Fresh page model", body: "Server rendering, islands, route contracts, layers, partials, and shared query cache.", href: "/web-layer/server/", icon: "O" },
{ eyebrow: "Overview & Concepts", title: "Fresh UI & design", body: "The copy-source component registry, design tokens, and the scaffolded dashboard app.", href: "/web-layer/fresh-ui/", icon: "O" },
{ eyebrow: "Quickstart", title: "Live dashboard", body: "Build a Fresh page backed by a typed SDK client and a cache-first QueryIsland.", href: "/tutorials/live-dashboard/", icon: "Q" },
- { eyebrow: "How-To", title: "Customize Fresh UI", body: "Adjust the generated UI layer and design-system surface.", href: "/how-to/customize-fresh-ui/", icon: "H" },
- { eyebrow: "How-To", title: "Server-validated form", body: "Build a form that validates and mutates on the server.", href: "/how-to/build-a-server-validated-form/", icon: "H" },
+ { eyebrow: "How-To", title: "Customize Fresh UI", body: "Adjust the generated UI layer and design-system surface.", href: "/web-layer/how-to/customize-fresh-ui/", icon: "H" },
+ { eyebrow: "How-To", title: "Server-validated form", body: "Build a form that validates and mutates on the server.", href: "/web-layer/how-to/build-a-server-validated-form/", icon: "H" },
{ eyebrow: "API Reference", title: "@netscript/fresh", body: "Generated symbols for the Fresh framework package.", href: "/reference/fresh/", icon: "R" },
{ eyebrow: "API Reference", title: "@netscript/fresh-ui", body: "Generated symbols for the companion UI package.", href: "/reference/fresh-ui/", icon: "R" }
] }) }}
+
+## Learn, do, look up
+
+{{ comp.cardsGrid({ columns: 4, cards: [
+ { eyebrow: "Learn", title: "Live dashboard tutorial", body: "Contract to page to live stream — the web layer end to end.", href: resolveXref("tut:live-dashboard").href },
+ { eyebrow: "Do", title: "Recipes", body: "Task-oriented recipes for this area, one problem each.", href: "/web-layer/how-to/" },
+ { eyebrow: "Look up", title: "`@netscript/fresh` reference", body: "Generated API reference. Related units: `fresh-ui`.", href: resolveXref("ref:fresh").href },
+ { eyebrow: "Understand", title: "Contracts & type flow", body: "The design rationale behind this pillar.", href: resolveXref("explain:contracts").href },
+] }) }}
diff --git a/docs/site/web-layer/interactive.md b/docs/site/web-layer/interactive.md
index f1c484588..0039c9032 100644
--- a/docs/site/web-layer/interactive.md
+++ b/docs/site/web-layer/interactive.md
@@ -2,6 +2,7 @@
layout: layouts/base.vto
title: Interactive islands
templateEngine: [vento, md]
+order: 7
---
# Interactive islands
diff --git a/docs/site/web-layer/query.md b/docs/site/web-layer/query.md
index dfbbf3436..eee644158 100644
--- a/docs/site/web-layer/query.md
+++ b/docs/site/web-layer/query.md
@@ -2,6 +2,7 @@
layout: layouts/base.vto
title: Data loading and the query cache
templateEngine: [vento, md]
+order: 4
---
# Data loading and the query cache
diff --git a/docs/site/web-layer/route.md b/docs/site/web-layer/route.md
index 74ced47d9..18c43b6f4 100644
--- a/docs/site/web-layer/route.md
+++ b/docs/site/web-layer/route.md
@@ -2,6 +2,7 @@
layout: layouts/base.vto
title: Routing and route contracts
templateEngine: [vento, md]
+order: 3
---
# Routing and route contracts
diff --git a/docs/site/web-layer/server.md b/docs/site/web-layer/server.md
index 9e74a359f..6d7112e63 100644
--- a/docs/site/web-layer/server.md
+++ b/docs/site/web-layer/server.md
@@ -2,6 +2,7 @@
layout: layouts/base.vto
title: The Fresh page model
templateEngine: [vento, md]
+order: 1
---
# The Fresh page model
diff --git a/docs/site/web-layer/testing.md b/docs/site/web-layer/testing.md
index 6142543fe..21e70a797 100644
--- a/docs/site/web-layer/testing.md
+++ b/docs/site/web-layer/testing.md
@@ -2,6 +2,7 @@
layout: layouts/base.vto
title: Testing Fresh pages
templateEngine: [vento, md]
+order: 10
---
# Testing Fresh pages
diff --git a/docs/site/web-layer/vite.md b/docs/site/web-layer/vite.md
index 2e35bb6ce..21bf97e3e 100644
--- a/docs/site/web-layer/vite.md
+++ b/docs/site/web-layer/vite.md
@@ -2,6 +2,7 @@
layout: layouts/base.vto
title: Build and Vite integration
templateEngine: [vento, md]
+order: 8
---
# Build and Vite integration