Skip to content

Deploy Ramose

Ramose deploys as a Cloudflare data plane declared in the same TypeScript stack as your application. Use separate Alchemy stages for development, preview, and production.

alchemy.run.ts
import * as Alchemy from "alchemy"
import * as Cloudflare from "alchemy/Cloudflare"
import * as Effect from "effect/Effect"
import * as Layer from "effect/Layer"
import * as Ramose from "ramose"
import { auth } from "./src/auth.ts"
const Store = Cloudflare.R2.Bucket("TasksStore", {
name: "ramose-tasks-store",
})
const Server = Ramose.Server("Tasks", {
main: import.meta.resolve("./peer.ts"),
storage: Store,
auth,
})
export default Alchemy.Stack(
"ramose-tasks",
{
providers: Layer.mergeAll(Cloudflare.providers(), Ramose.providers()),
state: Alchemy.localState(),
},
Effect.gen(function* () {
yield* Server
}),
)

Ramose.Server provisions the public Worker, Durable Objects, object storage, synchronization, and MCP route. The Worker entry installs the application definition:

peer.ts
import * as Effect from "effect/Effect"
import {
QueryReplicaDO,
createServer,
createTransactorDO,
deployOperationCatalogs,
} from "ramose/worker"
import { deployment } from "./src/domain.ts"
const operationCatalogs = await Effect.runPromise(
deployOperationCatalogs(deployment),
)
export default createServer({ operationCatalogs })
export { QueryReplicaDO }
export const TransactorDO = createTransactorDO(operationCatalogs)

The domain module exports deployment with the named schema as root and an explicit database deployment. Keep that module shared with the web application so both ends use the same definition identities.

Terminal window
bun alchemy dev

Local development uses the real Worker, Durable Object, object-storage, cache, WebSocket, and auth topology through Alchemy’s local runtime. Give test data unique database names instead of replacing infrastructure with in-process substitutes.

Use a non-production stage and explicit local identity configuration. Test instrumentation activates only when its flag and non-production stage guard are both present.

Terminal window
bun alchemy deploy --stage preview

A stage is an isolated copy of compute, storage, catalogs, and secrets. Do not point a preview application at production database storage. Set the identity provider’s issuer, audience, keys, callback URLs, and allowed origins for the exact stage URL.

Terminal window
bun alchemy deploy --stage prod

Catalog validation runs before incompatible definitions can reinterpret stored data. Prefer additive releases: deploy readers that tolerate both shapes, backfill, switch writers, then remove the old shape in a later release.

Production requires authentication and policy. The data plane is fail-closed when they are absent or invalid; do not add an unauthenticated fallback to make a health check pass.

Check liveness without relying on application data:

Terminal window
curl https://data.example.com/health

Then verify with real principals:

  1. An unauthenticated /db/* and /mcp request is denied.
  2. A member can open only its authorized root and child paths.
  3. A viewer’s query cannot infer filtered fields through relationships or counts.
  4. One permitted operation commits and appears in the browser replica.
  5. One forbidden operation produces a rejected receipt without revealing a hidden target.
  6. MCP discovery returns describe, query, and mutate, then an MCP mutation converges in the web app.

Destroying a stage removes the resources Alchemy owns and may delete its database storage. Export or preserve anything you need before teardown, confirm the exact stage name, and never use a broad production stage as a local scratch environment.