Skip to content

Programs

A program is your own server, run in a sandbox the Stack declares. Alchemy bundles it, uploads it into the sandbox and starts it with Node.js 24, then serves its port at https://<subdomain>-3000.on.boat.dev.

Give Boat.Sandbox a main and an implementation:

src/Api.ts
import * as Boat from "@alchemy.run/boat";
import * as Effect from "effect/Effect";
import * as HttpServerResponse from "effect/http/HttpServerResponse";
export default class Api extends Boat.Sandbox<Api>()(
"Api",
{ main: import.meta.url, type: "small", noEnv: true, public: true },
Effect.gen(function* () {
return {
fetch: Effect.gen(function* () {
return yield* HttpServerResponse.json({ ok: true });
}),
};
}),
) {}

Add it to the Stack and return its URL:

alchemy.run.ts
import * as Boat from "@alchemy.run/boat";
import * as Alchemy from "alchemy";
import * as Effect from "effect/Effect";
import Api from "./src/Api.ts";
export default Alchemy.Stack(
"MyApp",
{ providers: Boat.providers(), state: Alchemy.localState() },
Effect.gen(function* () {
const api = yield* Api;
return { url: api.url.as<string>() };
}),
);

The program listens on port (3000 unless you say otherwise), and reads PORT from its environment.

A deploy restarts the process, not the sandbox

Section titled “A deploy restarts the process, not the sandbox”

A program’s sandbox stays the same across deploys; only the process changes.

alchemy deployv1 uploadv2 uploadnode index.mjsin sandbox Apiv1 servingv2 servingalchemy deployv1 uploadv2 uploadnode index.mjsin sandbox Apiv1 servingv2 serving
Each deploy that changes the code or the program's env uploads the new bundle and restarts the process. The sandbox, its disk and its URL stay the same.

An unchanged deploy leaves the process alone. A program sandbox never stops on its own unless you set autoStop. If it stopped anyway, or rebooted, the next deploy resumes it and starts the program again.

env on a program sandbox is the program’s: changing it restarts the process, without replacing the sandbox.

Its URL needs a token unless it’s public

Section titled “Its URL needs a token unless it’s public”

A program’s URL needs its token unless you set public: true. The token is urlToken, sent as the _token query parameter or the _port_auth cookie:

const api = yield* Api;
return { url: api.url, token: api.urlToken };

See Ports & URLs.

A program uses bindings like any other host. Each gets its own key, which can do only what that binding needs:

Effect.gen(function* () {
const sandboxes = yield* Boat.CreateSandbox();
return {
fetch: Effect.gen(function* () {
const running = yield* sandboxes.list({ labels: { pool: "agents" } });
return yield* HttpServerResponse.json({ count: running.length });
}),
};
}).pipe(Effect.provide(Boat.CreateSandboxHttp));

To start sandboxes with a project already set up, see Snapshots & forks.