A NetScript app is five connected layers.
Contracts define the boundaries. Services serve them at request time, plugins extend them across processes, the web layer brings them to the browser, and the observed resource graph keeps the whole system visible.
The five layers solve different boundary problems. Contracts define operation inputs and outputs; services implement those operations; plugin manifests declare additional runtime resources; the web layer carries typed results to users; and Aspire connects the processes, dependencies, and telemetry. Read the sections in order for the map, then follow each section into the detailed documentation.
1. Contracts define the boundary
A contract describes behavior, not just data: checkout.start is as natural a boundary as a list
or update operation. It is the source of truth for an operation's input and output. A service
implements the oRPC contract, @netscript/sdk derives clients and query helpers from the same
contract map, and the service runtime generates OpenAPI from the contract router. The TypeScript
caller and the HTTP description share one definition, not two hand-written shapes.
Read contracts and type flow for the schemas and router mechanics.
2. Services execute request-time behavior
Contracts need a runtime. defineService() turns a contract router into a running Hono and oRPC
service. The preset wires in request logging, OpenAPI generation, a Scalar docs UI, RPC, health
endpoints, and service-info metadata. SDK clients resolve service URLs lazily from Aspire-injected
environment values, so callers do not pin a local port into application code.
A service owns the request boundary. Work that must resume after the process exits belongs in a durable runtime, not in a longer request handler. Go deeper in Services & SDK and service discovery.
3. Plugins add runtime capabilities
Services handle the request; plugins handle work that outlives one. The host is empty; plugins fill it. A plugin manifest is validated data that declares services, background processors, schemas, topics, configuration, and telemetry contributions. The CLI turns those declarations into static workspace wiring.
“One plugin” is a packaging unit, not a runtime unit. Aspire can materialize a plugin's API and background processors as separate resources. Workers, sagas, triggers, and streams are the durable examples; the full manifest and registry mechanics live in the plugin-system explanation.
4. The web layer carries the boundary to the user
The contract reaches the browser through the web layer. @netscript/fresh builds on Fresh 2 and
Preact. Typed route contracts and definePage() connect paths, search parameters, metadata,
handlers, data layers, and forms. A server page can call the contract-derived SDK, hydrate TanStack
Query state, and hand interactive work to islands; routes that need no island ship no island
JavaScript.
Use the web-layer overview for the complete model, then follow the focused guides for server rendering, page builders, and queries and islands.
5. Observability connects the resource graph
The diagram zooms into one plugin. At workspace scale, the generated AppHost also composes apps, services, plugin processors, databases, and cache resources, then injects service-discovery and OTLP configuration into their processes. The Aspire dashboard reads health, logs, traces, and metrics from that running graph; trace context carried by SDK calls keeps client and service spans connected.
Aspire is the default local orchestration layer, not part of every package API. Without it, the Deno packages remain usable, but resource startup, discovery values, and telemetry wiring become the application's responsibility. Read Orchestration & runtime, Observability, or the Aspire boundary for mechanics.