Anatomy of a deployment

A deployment is everything RTEdge.net keeps and serves for one route after a deploy. It is built from one playground state: your HTML, its settings and its worker. This article describes the parts of a deployment, how your source becomes each of them, and when a deploy becomes visible.

Overview

PartServed atMade from
Root documentroute/Your HTML, after worker removal, edge:remove and edge:redact, and attribute stripping.
Exported filesroute/nameElements with o:=name, and images with o:= and a data: source.
Edge workerroute/:… and fallbacksEvery <script type=worker edge>, merged.
MetadataNot served as suchThe owner, the editors, the deploy time and digest, the last saver and a worker flag.
VersionThe version viewer, opened with route/??The deployed state: source, served page, files and worker code.
ScreenshotThe default og:imageA capture of route/.

From source to deployment

A deploy sends the whole playground state to RTEdge.net: your HTML, the title, the routes, the editors and the view settings. For every route in the Routes field, RTEdge.net compiles that same state and publishes it. The stored state is exactly what route/? reopens later.

The compile step, in short:

  1. It reads your HTML and collects the worker:fs assets and the <script type=worker edge> elements.
  2. It works out who may view the deployment and who may open its source, from edge:user, io:user and io:guest.
  3. It removes every <script type=worker> from the page. Each one becomes blank lines, so later line numbers do not shift.
  4. It merges the edge scripts, bundles their static imports, prepares WebAssembly and uploads the worker.
  5. It extracts the o:= exports, applies edge:remove and edge:redact, and strips the RTCode attributes from the page and the exports.

Deploying to several routes compiles the state once for each route, and each route is charged in full. See Limits and quotas. Ownership and quota checks run for all routes before anything is written, and then every route is published on its own, so one route can fail while the others succeed.

The root document

What is served

The root document answers GET only. Its body is the platform's prefix, <!DOCTYPE html><!--<Ϟ>←<λ>→<Ϟ>-->, followed by the generated social tags and then your page. It therefore always starts in standards mode, so do not write a doctype yourself. Social previews and screenshots describes the generated tags.

Attributes that disappear

Every attribute whose name starts with i:, o:, io:, edge:, h: or worker: is removed from every element of the page and of every export, also inside templates. Other attributes stay, such as oi:exclude, copy:redact, sync, pure, reload and ai. An element whose only attributes were prefixed remains as a bare tag: <meta io:sync=…> is served as <meta>. Code that looks for these attributes at run time works in the playground but not on a deployment.

Redacted and removed elements

edge:remove

Replaces the element with one newline fewer than the lines it spanned, so the line count stays the same. A one-line element leaves an empty line.

edge:redact

Replaces the element with <!--REDACTED, the same number of lines, and -->. There is no space after <!--.

Both apply to the page and inside exported content. An element with its own o:= or worker:fs is still exported. A worker:fs element stays in the served page, with only its attribute removed, unless you also mark it edge:remove or edge:redact. copy:redact has no effect on a deploy.

Warning: Redaction is not secrecy. edge:redact and edge:remove only change the served HTML. The deployed source stays visible through ? unless access to it is restricted, and every deploy also saves the full source as a version. See Keeping secrets out of deployments and Restricting access.

Markup the deploy rewrites

When a deploy changes the page (a stripped attribute, a removed worker, a redaction), or the route has a worker, the page is served as the platform's re-serialization of your HTML. Attribute values get double quotes, SVG children become self-closing, and the line count is preserved. Otherwise your exact text is served. Exports taken from the innerHTML or outerHTML of an element, such as templates, are always re-serialized, and the text of <style> and <script> exports is served as you wrote it.

An empty page

A page that is empty after processing is handed to the worker, when the route has one, so a route can be an API only. Whitespace counts as empty, and on a route at the root of a host so do comments. Without a worker, a page with only whitespace answers 404, "File does not exist."

Exported files

An element with o:=name becomes the file route/name for every route you deploy to.

Try files, headers and a hidden page

Deployed to name.rt.ht, the root keeps its <style> without attributes, the template becomes a four-line comment, the iframe becomes a blank line, /.css answers text/css with a cache header, and /About.html answers text/html and gets a doctype when you navigate to it.

html
<title>Demo</title>
<style o:=.css h:cache-control="public, max-age=300">
  body { font: 1rem/1.5 system-ui; color-scheme: light dark }
</style>
<template o:=About.html edge:redact>
  <meta charset=utf-8><link rel=stylesheet href=.css>
  <h1>About</h1>
</template>
<iframe src=About.html edge:remove></iframe>
<a href=About.html>About</a>

The edge worker

See The edge worker runtime.

Metadata and the digest

RTEdge.net keeps a small record for each route: the owner, which is the first account that deployed it, the editors, the deploy time with a digest, the last saver, and whether the route has a worker. The saver is the editor who deployed last. It disappears when the owner deploys again.

The digest changes on every deploy, even when the content is identical, so it detects change rather than describing content. Pages and files send the deploy time as their deploy response header, and route/::meta returns the digest hash. Experimental See The ::meta suffix.

Detect a new deploy

A page can poll ::meta and reload when the digest changes. The request resolves against the page's directory, and it exists only on deployed routes, so the check stays idle in the playground.

html
<script type=module>
  const digest = () => fetch('::meta', { cache: 'no-store' }).then(r => r.json()).then(j => j?.digest?.hash, () => null)
  const last = await digest()
  last && setInterval(() => digest().then(now => now && now !== last && location.reload()), 30_000)
</script>

The version

Each deploy is also recorded as a version of the route: the stored playground state, your source, the document that was served, the exported files and the worker code. A few seconds after the deploy the version shows up in the version viewer, because it is written in the background. Worker modules and WebAssembly files that were fetched at deploy time are not kept, only their names. Deleting a deployment records a deletion, and keeps the older versions. Versions and Smart Undo shows how to browse, compare and restore them.

The screenshot

Every deploy queues a screenshot of route/. It is taken at a 1200 by 630 CSS pixel viewport, at twice the scale, so it is a 2400 by 1260 PNG, once the page's network requests have settled. Areas the page does not paint are transparent. The image is kept with the version history, and it is the default og:image of the page. Only the root document gets one. Social previews and screenshots has the details.

Freshness after a deploy

Every request reads the deployment from globally replicated storage, which is eventually consistent. Other locations can show the previous version for a short while after a deploy or a delete. Cloudflare documents up to about 60 seconds for this kind of storage.

For 90 seconds after the owner's latest deploy or delete, two kinds of request read the owner's fresh copy instead: requests that carry Cache-Control: no-cache or max-age=0, which is what a browser reload sends, and document navigations whose referrer is https://rtcode.io/, such as the links in the deploy toasts. The Edge: Open command opens routes without a referrer, so its first load can still show the previous version. Reload to see the new one.

Pages are sent with cache-control: no-store. Exported files have no cache-control unless you set one with h:cache-control. A signed-in deployer who reloads a route that was just deployed for the first time sees it at once, while other visitors can see "Code does not exist." until it has propagated.

Guarantees and limits

Request routing

How a URL finds its route, and which part of the deployment answers it.

Security and access model

Who can view a deployment and who can open its source.

Playground and edge differences

What changes between the live preview and the deployment.

Architecture

How the editor, the output and the edge fit together.

See also