A Workers-first foundation for the Kalaris Labs website
How Kalaris Labs combines prerendered Astro pages, static assets, and a small request-time API in one Cloudflare Workers deployment.
The Kalaris Labs website has two different jobs. It publishes content that should load as plain, crawlable HTML, and it handles a small number of request-time actions such as the contact form. Those jobs do not require two hosting platforms.
We use Astro with the Cloudflare adapter so prerendered pages and Worker routes ship through one deployment. The architecture keeps the default path static while preserving a narrow server boundary where it is needed.
Prerender content by default
The home page, company pages, blog entries, and research notes are generated during the build. A crawler or browser receives complete HTML without waiting for client-side rendering. Images, styles, and bundled scripts are emitted as immutable assets.
Each content entry comes from an Astro content collection with a validated schema. Titles, descriptions, publication dates, update dates, tags, draft state, and social images are checked before a deployment can complete. Dynamic article routes use getStaticPaths() to create one canonical URL for every published entry.
export async function getStaticPaths() {
const entries = await getCollection('research', ({ data }) => !data.draft);
return entries.map((entry) => ({
params: { slug: entry.id },
props: { entry },
}));
}
Prerendering gives content pages a simple failure model. If an article cannot render or its metadata violates the schema, the build fails before the page reaches production.
Keep request-time behavior small
The contact endpoint runs at request time because it validates user input, verifies a Turnstile token, and sends mail through Resend. It lives in the same Astro project but does not force the rest of the site to render dynamically.
The route accepts a constrained payload, performs server-side validation, and accesses secrets only in the Worker environment. Public build configuration remains separate from credentials. This boundary is easier to review than a general-purpose backend attached to every page request.
One deployment boundary
Cloudflare Workers Static Assets serves the generated files while the Worker handles the routes that need computation. A wrapper applies the canonical-host redirect so www and alternate hostnames do not create duplicate public URLs.
The deployment contains:
- prerendered HTML for public pages;
- fingerprinted CSS, JavaScript, fonts, and images;
/api/contactfor server-side form processing;- security and discovery files such as
robots.txt, RSS, and the sitemap; and - shared response headers for caching and search discovery.
This arrangement reduces configuration drift. The code that defines a route, its build output, and its deployment target changes together.
JavaScript is an explicit cost
Astro components render to HTML unless a component receives a client directive. We use React only for interactions that need browser state or animation. Headers, footers, article bodies, cards, metadata, and structured data remain server-rendered.
This improves more than speed. Search engines and assistive technology receive the primary content in the document, and article links remain ordinary anchors. The site continues to communicate if an enhancement script fails.
Local and production parity
Two development commands expose different boundaries. The standard Astro development server is useful for content and component work. A Worker-oriented command builds the project and runs it in the local Cloudflare runtime for API, bindings, and deployment behavior.
Before release, the pipeline performs type checking, linting, an Astro production build, and a Worker dry run. This catches content-schema failures, missing imports, and platform-specific configuration before deployment.
Limits of the approach
A single Worker deployment is appropriate while request-time behavior remains small and stateless. A large application with long-running jobs, independent service lifecycles, or different security domains may need separate services. The current architecture does not prevent that split; it avoids paying its operational cost before the boundary exists.
For the content layer, see the research collection and engineering blog. For the broader infrastructure thesis behind Kalaris Labs, read the manifesto.
