Skip to content
ReleaseRelease candidate

DaloyJS: The First Release Candidate

What the release candidate means: bug fixes and docs only until stable. This post covers what landed across the beta train (including MCP), and what remains before the stable release.

Devlin Duldulaosoftware engineer & published book author7 min read

A couple of weeks ago I wrote a post whose whole punchline was that the beta changed nothing on purpose. Today I get to write the sequel, and the tone is different. We just tagged the first release candidate. If the beta was me tentatively hoping the API was ready, the RC is me locking the door and putting the key on the table.

The release-candidate rule is short and unglamorous: only bug fixes and documentation land before stable. Middleware, adapters, and helpers are frozen. The public API you see in the release candidate is the public API you get at stable, minus whatever bugs you help me find. That is the entire promise, and it is the reason an RC feels heavier to tag than a beta.

So the beta really was the freeze

When I said nothing changed at beta.0, I meant the shape was set. But betas .1 through .7 were not a nap. They were the part of a release where you stop building and start proving, and a few real things landed in that window that are worth calling out before the door shuts.

The biggest one: DaloyJS grew a dependency-free Model Context Protocol server. If you are building anything an AI client talks to, you can now expose tools, resources, and prompts over MCP Streamable HTTP from the same framework, with the same body limits, timeouts, auth middleware, and problem+json errors as any other route. It validates Origin against DNS rebinding by default, because that is a spec requirement and also because leaving it off is exactly the kind of quiet footgun this framework exists to remove.

ts
import { App, createMcpHandler, mcpRoutes } from "@daloyjs/core";
import { serve } from "@daloyjs/core/node";

const mcp = createMcpHandler({
  serverInfo: { name: "inventory-mcp", version: "1.0.0" },
  // Origin is validated against DNS rebinding out of the box.
  // Add browser origins you actually trust; everything else gets 403.
  allowedOrigins: ["https://app.example.com"],
  tools: [
    {
      name: "inventory_lookup",
      description: "Look up available units by SKU.",
      inputSchema: {
        type: "object",
        properties: { sku: { type: "string", minLength: 1 } },
        required: ["sku"],
        additionalProperties: false,
      },
      handler: async ({ sku }) => `SKU ${sku}: 42 units`,
    },
  ],
});

const app = new App();
for (const route of mcpRoutes("/mcp", mcp)) app.route(route);
serve(app, { port: 3001 });

The rest of the beta window was the unglamorous work I actually respect most. The Node hot path got faster (lazy request and response shims plus sync-first validation, which is a 21% bump on the full-contract benchmark and 53% on the bare echo path, with zero behavior change and every security check still in place). The create-daloy templates were brought into line with the security and contract guidance they ship with, so a freshly scaffolded app actually follows the patterns the docs preach. And the docs site itself got a navigation and hydration pass so reading it stops fighting you.

Nothing to do if you are already on the beta

The release candidate is a straightforward upgrade from the beta. Updating is a lockfile change and a good night's sleep. We moved@daloyjs/core, create-daloy, and the JSR package @daloyjs/daloy together, as always, and every create-daloy template points at the matching core package. A plain install gets you the RC, no dist-tag archaeology required:

bash
# Scaffold a fresh project on the release candidate
pnpm create daloy@latest my-api

# Or add the core to an existing project
pnpm add @daloyjs/core

# npm works too
npm install @daloyjs/core

What remains before stable

I could pretend a release candidate means we are basically done. We are not, and the roadmap says so out loud. The engineering bar is met: the API has been additive across the whole beta train, coverage sits around 99% lines and 92% branches, the supply-chain gates are green, and the benchmark suite is public. What is left before stable is deliberately not code.

The rest is judgment, not a checklist. I want the security disclosure process to get one real exercise, because an untested policy is only a file. And I want the migration story to be solid for the framework most people are actually leaving, which is why the Express guide came first and got the most attention. Everything past those two is polish I can ship inside a stable line instead of holding the stable line hostage to it.

Update, 29 July 2026. This section first listed three production users as a hard gate, plus Fastify and Hono migration guides. Both are gone. The production-user gate turned out to be circular: sensible teams do not put a release candidate into production, so waiting for production users before dropping the -rc suffix meant waiting for something my own version number was preventing. Developers trust a stable line, and that trust is what produces the field feedback I was trying to collect. The Fastify and Hono guides are still wanted, just moved to the standing docs track. The roadmap records the amendment in full, including what I give up by freezing the API without production feedback first. First drafts of a plan are written before you have uncovered everything, and this one was no exception.

What I want from you

Same ask as the beta, higher stakes. A release candidate is a bet that the API is right, placed in public so you can call it. The most useful thing you can do in the next few weeks is build a real thing on the release candidate and find the corner I sanded wrong. Report the bug. Tell me the name that reads badly. Show me the adapter that drifts from the docs. Once the stable release ships, that feedback costs a deprecation cycle to act on. Right now it is free.

If you are new here, start with the case for using DaloyJS today and the defenses you inherit on your very first route in Secure by Default. Then run pnpm create daloy@latest and come tell me what broke.

Tagging a release candidate is the moment a project stops being "almost ready" and becomes "prove it." That is the part that makes me nervous, and I would rather be here nervous than still adding features I would have to defend forever. Thanks for being early. Let us go find the bugs.

About the author: Filipino developer in Norway who has cut enough releases at odd hours to know the scary ones are the boring ones with a big version number.