Skip to content
BetaDocs

Publishing with your own accounts

This workflow applies to Typeroll Cloud and self-hosted installations with customer publishing enabled. Cloud users connect their own accounts; Typeroll operates the publisher authorization service. Self-hosting operators must first configure the publisher Apps, OAuth and private draft storage for their portal. See Cloud and self-hosting. The GitHub App’s callback URL and Setup URL are listed under GitHub connection troubleshooting.

You can create sites, connect an AI agent, edit pages, save drafts and share an expiring preview before setting up publishing. With customer publishing enabled, new uploads use the hosting operator’s private draft storage until your R2 access is ready. Public deployment stays locked and links to Publishing with the missing setup steps. A public website address appears after the exact deployment has been verified at that address.

Your hosting operator must enable customer publishing and configure private draft storage. Existing managed sites retain their existing publishing mode until they are explicitly migrated.

Starting with Core 0.1.99, both the site overview and editor check Publishing before enabling deployment. The check includes GitHub, the site’s Hosting Group, the selected version’s website address, required media storage and hosts, and the selected build engine’s completed verification and required capabilities. An older build engine can need an update even if its previous setup succeeded. Select the listed Publishing → Builds link and finish build setup to update and verify it.

Deployment stays disabled while the check runs or cannot be completed. Missing steps link directly to their settings. Return after correcting them, or select Check again after a check error. The server checks again before accepting a job, including deployments requested through the public API and MCP.

Saved publication status still determines which pages enter the next build; it does not make a page live. Editing, saving and Preview remain available. A new custom domain may be prepared before traffic DNS is switched: the candidate must still be built and verified before cutover. External service failures can still interrupt a build after these setup checks pass.

Starting with Core 0.1.95, importing a website, external media, a sitemap or Search Console data requires the organization’s own verified storage. Select Publishing → Media storage to connect it before starting an import. This requirement applies in the UI, public import API and MCP; imports never fall back to the operator’s draft bucket. GitHub and website domains can be connected later.

Typeroll automatically provisions a media transfer Worker in the organization’s Cloudflare account. The Media transfers status shows readiness or an actionable setup error. Existing OAuth connections may show Allow media transfers in Cloudflare; approve that permission once. No replacement R2 keys or GitHub build connection is needed. Source files travel directly to customer storage and are verified there, regardless of the selected build provider.

Open the sidebar, select the correct Organization, then select Publishing under Account. On mobile, open the menu first. Organization owners and admins manage these connections.

  1. Select Connect GitHub, sign in to your personal account or as an owner of your GitHub organization, and authorize Typeroll. When you return, the GitHub card shows who you are signed in as and every account that has the Typeroll GitHub App, with the reason each can or cannot be used. If the App is missing, select Install the GitHub App, choose the account that will own your repositories, select All repositories, and approve the installation. Typeroll returns automatically to verify access. If GitHub stays on its own settings page, go back to the Publishing tab: the card checks again by itself and, for your personal account, offers Connect @your-login, which GitHub asks you to confirm once. Choose an account if more than one is available. The card confirms Connected when all checks pass; signing in alone is not completion. No account name or ID needs to be typed. If something blocks the connection, see When Connect GitHub does not finish.
  2. Select Connect Cloudflare, sign in and approve the account. Cloudflare lists the permissions Typeroll asks for; see Cloudflare permissions for what each one enables. Optional DNS and URL rewrite permissions let Typeroll configure domains automatically. You can decline these and let your administrator or AI agent manage DNS. If you select several accounts on Cloudflare, choose one in the card. If something blocks the connection, see When Connect Cloudflare does not finish.
  3. Typeroll checks R2 automatically. If activation is required, follow the account link to Storage & databases → R2 object storage → Overview and complete Cloudflare’s subscription checkout. Return and select I’ve activated R2 — check again. Typeroll creates separate private-original and public-media buckets and configures browser uploads.
  4. In R2 object storage → Overview → Account Details → Manage API Tokens, create an Account API token with Object Read & Write for both buckets shown in Typeroll. Enter its Access Key ID and Secret Access Key, then select Verify keys and finish setup. These are different from the API token value. A Super Administrator can create account tokens; a User API token is an alternative whose access depends on that user’s continued membership.
  5. In Builds, select the organization’s build provider and complete its shared-engine setup. Cloudflare builds need a one-time builder repository connection and build token; GitHub Actions uses the existing Typeroll App connection. See Cloudflare hosting and builds.
  6. Under Organization domains, choose a Cloudflare domain from the connected account. Enter short labels in Media subdomain and Sites subdomain, such as media and sites. The complete addresses appear below the fields. Select Configure domains to connect the media hostname to R2 and queue media migration. HTTPS activation is checked automatically. Each site and version gets its own website address when you publish.

The guided GitHub installation continuation is available in Core 0.1.78 and later, once your hosting operator deploys that release. Earlier versions require you to install the publisher App and then select Connect GitHub again. Cloud production and staging use separate publisher Apps: approving one does not install the other. The required installation step survives reloading Publishing. If your GitHub organization requires an owner’s approval, return after approval and select Check again to verify access without signing in again.

Starting with Core 0.1.79, Finish build setup checks Cloudflare build access before creating the build project or changing build storage. A connected Cloudflare account can still need separate permission for Workers Scripts and Workers Builds. If the Builds card says Build permissions required, select Approve build permissions, approve in Cloudflare, then return and select Finish build setup. Hosting and media remain connected.

Starting with Core 0.1.80, returning from Cloudflare approval automatically rechecks the Builds card even after the connection card clears the callback address. If an older error remains on an earlier version, select Refresh status. A stale message does not mean that you need to approve access again.

When a test build finishes, its saved status changes in the background. The Builds card recovers a conflicting status check by reading the latest result; it does not start setup or another test build again. On Core 0.1.100 and earlier, if Build settings changed. Reload and try again. remains beside Running a test build…, reload Publishing once to see the saved result. This message alone does not mean the test build failed or that you need to reconnect accounts.

On older versions, if setup shows HTTP 403, select Refresh status first to show the permission action. If setup fails, it has stopped: the card should show the failed step and next action, rather than a continuing preparation message.

In Core releases after 0.2.60, the GitHub card shows five steps, each with its own status symbol and text: 1. Publisher ready, 2. Sign in to GitHub (with the GitHub account you signed in as and Use a different GitHub account), 3. Account with the GitHub App (every account GitHub showed, with its reason and fix), 4. Access & permissions and 5. Connected. One Next step box at the top names who must act and offers the one button that fixes the current blocker. If a personal account needs renewed authorization, the sign-in step identifies it without asking you to reinstall the App. Earlier releases show two steps, 1. Sign in to GitHub and 2. Install the GitHub App, and report problems in a banner at the top of the page.

Cloudflare sign-in and R2 upload access are separate. The current OAuth connection can prepare storage but cannot create the S3 upload key pair. Create the keys once using Manage API Tokens and save them in Media storage. After the upload check succeeds, reuse those keys for the organization’s sites; you do not need to create new keys for each site or reconnect a working account.

Use the refresh icon beside Cloudflare domain to retrieve newly added domains. Refreshing keeps the existing account connection. An empty result does not mean you need to reconnect. If the saved OAuth grant lacks domain permissions, Allow domain access appears. It opens Cloudflare’s approval screen and keeps the existing account and R2 setup. Typeroll will not replace an existing DNS destination or an existing shared media hostname through this setup action.

Connect Cloudflare asks for these permissions for the Organization’s account and for each Hosting Group’s account:

Cloudflare permission What it enables Requested
Cloudflare Pages (edit), Account Settings (read), offline access Create and publish each Site’s Pages project and keep the connection signed in Always
Workers R2 Storage (edit), Workers Scripts (edit) Organization media storage and its upload service Organization connection only
Zone (read), DNS (edit), Cache Purge, Zone Transform Rules (read, edit) Configure domains and DNS records automatically; optional Always; you may decline
Account Settings (edit) Create a Site’s Cloudflare Web Analytics site when you enable Analytics for that Site When the installation requests it
Account Analytics (read) Read Web Analytics reports for Insights When the installation requests it
Dynamic URL Redirects (read, edit) Create the Single Redirect rule for redirect hosts such as www When the installation requests it

The last three are part of onboarding when the installation’s operator has enabled them for its Cloudflare sign-in; a self-hosted operator does this as described in Self-hosting. Approving them turns Analytics on for no Site. You choose which Sites measure traffic under Publishing → Analytics or in a Site’s Settings → Core modules; only those Sites get a Web Analytics site and a beacon after their next publication.

A connection made before these permissions were requested shows Permission update available on its Cloudflare card (the Cloudflare tab for the Organization’s account, the Hosting Groups tab for others). An owner or admin selects Approve update once. Typeroll first confirms that the approval only grants permission, then opens Cloudflare. The approval keeps every permission already granted, the account, Sites and media, and covers every current and future Site in that Hosting Group. When several connections of the Organization use the same Cloudflare account, one Approve update renews all of them: each connection keeps its own Cloudflare authorization, so Cloudflare may show its approval page once per connection, and Typeroll continues with the next one automatically.

The Analytics module creates each site’s Cloudflare Web Analytics site in its Hosting Group account and reads Insights from there. On an installation that does not request the Analytics permissions during onboarding, select Approve Analytics access on the site’s Analytics module when it asks, and approve Account Settings (edit) and Account Analytics (read) in Cloudflare.

For external DNS or agent-managed settings, open Manual settings or external DNS. Enter the complete Site address base and Shared media host, choose Me or my AI agent, and save. Read Setup instructions, apply the records, then select Check domain status.

Each Hosting Group owns a Cloudflare hosting account and a site address base. Default is created automatically and reuses the organization connection. One group is enough to get started; add another only when you need another Cloudflare hosting account. Its neutral gray card means that no separate setup is needed here. Configure the shared connection and address base in Cloudflare account and Domains. The Organization owns shared media, DNS access, GitHub and the build-provider selection. Each site gets its own generated private repository and static Pages project. A new site’s repository is named typeroll-<site id> (with a short suffix if that name is taken); sites published before Core 0.2.133 keep their existing hash-named repository. Adding a site or version reuses the organization connections.

Account ID is only needed in advanced token setup. In Cloudflare, select the account and use Search → Copy account ID, or Workers & Pages → Account Details → Account ID. Copy the 32-character account value, not a Zone ID.

You can use your personal account for both site repositories and the shared build repository. It does not need to become a GitHub organization. Typeroll still manages these connections once per Typeroll Organization.

Typeroll securely stores your authorization to create new repositories and renews it when needed. Publishing to existing repositories and version branches uses the App installation. If you revoke authorization or leave it unused for six months, open Publishing → GitHub account → Personal account authorization and select Reconnect GitHub. Existing repositories are kept. No personal access token needs to be copied into Typeroll.

Typeroll keeps the result of your latest GitHub attempt for 24 hours and shows it in the GitHub card, even after you reload. Each reason starts with Who acts, naming who can fix it:

  • You — for example sign in with another account, authorize single sign-on for an organization, allow All repositories, or install the App.
  • GitHub organization owner — you are a member but not an owner, or an owner must approve the installation or a permission request.
  • Typeroll publisher — the operator of this Typeroll installation must finish its GitHub App or encrypted credential storage settings.
  • Typeroll administrator — the GitHub account is already connected to another Typeroll Organization.

The button beside each reason opens the exact GitHub page or starts the right step. After fixing something on GitHub, return to Typeroll: the card checks again automatically when the page opens or you return to its tab, or select Check again. Checking again uses the publisher App’s own access and the GitHub account you signed in with during the last hour, so you do not need to sign in again; after an hour the card asks you to sign in. Connecting a personal account still needs a sign-in. If you installed the App on an organization the card does not list, open Installed the App on another organization? and enter its GitHub name.

A common case: you own one GitHub organization but installed the App on another one where you are only a member. The card lists that organization with GitHub organization owner and the reason. Install the App on the organization you own instead, or ask an owner of the other organization to connect GitHub.

Every reason, with its fix, is listed in GitHub connection troubleshooting.

The Cloudflare account card, and each Hosting Group card for its own account, explains every result inside the card and keeps it for 24 hours, so a reload shows the same explanation. Its steps are Publisher ready, Sign in to Cloudflare, Choose account, Access verified and Connected, and the box at the top names the one next step. Each reason starts with Who acts: You, a Cloudflare account administrator (a Super Administrator who can change your role, for example to allow Cloudflare Pages), an owner or admin of the Organization that uses the account, the Typeroll publisher or a Typeroll administrator.

If you authorized several accounts, the card lists them and you choose one within 10 minutes; Typeroll never chooses for you, because sites and media are created in the connected account. Within an hour of signing in, Check again offers an expired choice again, or finds a fixed role, without another sign-in. On a connected account, Check again re-verifies the authorization and Pages access. Every reason is listed in Cloudflare connection troubleshooting.

Several Organizations in one Cloudflare account

Section titled “Several Organizations in one Cloudflare account”

Several Organizations may use the same Cloudflare account, for example when one owner keeps each company in its own Organization so that a site can be transferred later. Connect the account in each Organization while signed in as a person who is an owner or admin of an Organization that already uses it. The card then shows This Cloudflare account is also used by: …, naming the Organizations you administer, and asks you to confirm. Sites stay separate: each Organization keeps its own Pages projects, media buckets, media hostname, build engine and settings, and Typeroll refuses to change a resource another Organization owns. Disconnecting removes only that Organization. People who do not administer an Organization that uses the account see that the account is in use, never which Organization uses it. GitHub accounts are not shared this way: a GitHub account still belongs to one Organization. See Sharing one Cloudflare account.

While GitHub is connected, Typeroll keeps using the same GitHub account. To move new sites to another account, select Disconnect GitHub, then Connect GitHub and sign in. The card shows the previously connected account and asks you to confirm the change with Use … instead of …. Existing repositories stay in the previous account and are not moved. Sites that already publish from the previous account may need to be set up again. An account connected to another Typeroll Organization cannot be connected here.

An existing connection does not need to be disconnected or connected again. In Publishing → GitHub account, select Approve GitHub update when it appears. This opens the existing installation in your personal account or GitHub organization. Review and accept the requested permissions as the personal account holder or a GitHub organization owner, then return to Typeroll. Typeroll checks automatically and confirms when the permissions are up to date. Check GitHub permissions repeats the check.

If Typeroll says it needs to enable GitHub build access first, your hosting operator must update the publisher App registration before an approval is available. You do not need to edit an App, create a token or reconnect. Your existing connection remains active. Permission approval alone does not change the selected build provider.

R2 media domains must belong to a zone in the same Cloudflare account as the bucket. You do not need to move your root website, email or unrelated subdomains. Cloudflare’s partial CNAME setup connects selected hostnames while keeping your existing authoritative DNS provider and nameservers. It requires Business or Enterprise. Selecting external DNS management in Typeroll means you or your agent manage the records, including records in Cloudflare; it does not bypass this provider requirement. If your domain already uses Cloudflare DNS, keep other records pointing at their existing services. A CNAME pointing to an r2.dev address is unsupported. See R2 custom domains and partial setup availability.

Shared media domain status distinguishes missing Cloudflare access, unfinished R2 setup, an unattached or disabled media domain, pending ownership/certificate activation, and an active Cloudflare media domain. Provider errors are shown as an unknown status, not as proof that a working domain is disconnected. The panel names the connected account and public bucket and records when it was checked.

For manual setup, open Cloudflare → R2 object storage → the public bucket → Settings → Custom Domains → Add. Enter the shared media hostname, select Continue, review the record and select Connect Domain. Use Manage DNS in the connected domain’s menu to view the record. With partial setup, also apply Cloudflare’s supplied verification and CNAME records at your DNS provider. The UI links to Cloudflare’s current instructions; do not guess a target or point the hostname at the S3 API endpoint.

An active media-domain status does not verify every site address or publish a site. Website and version addresses have their own DNS requirements and public verification in the site’s Publishing flow. The address base itself does not serve a website; generated addresses are below it. Deployment verifies actual published media files before exposing the live link.

The organization API GET /api/v1/publishing/domains and MCP read_organization_publishing_domains return the same domain_status, including state, checked_at, zone_check, certificate/ownership state and structured steps. Set sites_domain and media_host independently using the corresponding PUT route or set_organization_publishing_domains. Legacy default_domain configurations retain their old addresses until explicitly updated. Changing the site address base affects subsequent deployments; an existing shared media host cannot be replaced by a plain settings overwrite.

To use the same automatic setup from an agent:

  1. Call GET /api/v1/publishing/zones or MCP list_organization_publishing_domains to list available domains.
  2. Read the current configuration revision from the domains endpoint.
  3. Call POST /api/v1/publishing/domains or configure_organization_publishing_domains with revision, zone_id, media_subdomain and sites_subdomain. The labels contain no dots or URL.
  4. Poll the domains endpoint while domain_status.state is pending. A setup_error means settings were saved but provider setup needs attention; use the returned revision when retrying.

These endpoints require an organization API key. Site keys cannot discover or configure organization domains. Setup creates the selected R2 media connection; it does not publish a website or modify unrelated DNS records.

Choose the site and its Version, save changes and select Deploy. The main version publishes the live site. Other versions use version-<version-id> Git branches and separate public test addresses under the organization’s site address base. Those versions and default-domain demos use noindex.

Typeroll freezes the selected version, including inherited blocks, templates, content types, templates and runtime settings, and pushes generated source to GitHub. The selected shared build engine installs the pinned dependencies and runs Astro. The resulting static files go to the Site’s Hosting Group on Cloudflare Pages. While the build or domain propagation is pending, the interface shows progress. The link appears automatically after verification.

The repository is generated output. Edit through Typeroll’s UI, API or MCP. Manual repository changes are unsupported and may be replaced by a publication.

Starting with Core 0.2.4, customer publishing separates source capture, the Git commit and build dispatch into short, resumable steps. Typeroll reads a consistent view of the selected version and its inherited content, excludes private drafts, and freezes the public source with its renderer and destination addresses. Edits saved after this snapshot belong to a later deployment.

The customer’s GitHub or Cloudflare engine resolves website and media addresses, prepares images, builds the static site and verifies its output. Waiting for that engine does not keep a CMS request open. Retries reuse the saved snapshot and Git commit. Leave the page open or return later; UI, API and MCP show the same job. Do not start a duplicate deployment just because the external build is still running.

Generated repositories keep pages, templates and other content records in content/, indexed by publication.json. Unchanged Git files are reused; removed pages and unsupported manual files are removed from the generated tree. The build publishes a complete static site, with unchanged HTML reused when the partial-build cache is available. Images stay in R2, outside Git. The repository contains its frozen renderer, content, address mappings and integrity manifest, so independent builds do not fetch mutable content from the CMS.

The CMS still reads and validates the public source before handing it off. Large sites and provider latency can take longer; this is not a fixed publication-time guarantee. API/MCP status can include job.coordinator with the last completed CMS checkpoint and its attempt duration. It excludes time waiting for an external build and is not the site’s total build duration.

For customer publishing, dry_run validates and prepares the source without pushing Git or starting an external build. It does not prove that Astro will build successfully. Other self-hosted deployment adapters may run a local build without uploading it. Check the job’s phase to see which validation completed.

Open Site settings → Publishing → Website and media addresses. Set the website host and, optionally, a separate media host. For example:

Setting Example
Organization shared media host media.example.com
Site website host www.customer.com
Site redirect hosts customer.com, answering 301 to the website host
Site media host images.customer.com
Media path prefix Empty for a separate media host; /media when sharing the website host

Before a site has its own media host, an image can use https://media.example.com/media/<site-media-id>/photo.png. With a separate site media host it can use https://images.customer.com/photo.png. Referenced public media is included in the static site output; shared media aliases remain available through organization storage and configured URL rules. No dynamic Worker serves the website or the images.

A media item’s public_path can preserve a legacy path such as /wp-content/uploads/2024/photo.jpg. The path must end in an extension that matches the file’s type. Public paths are immutable: a different file must use a new path. This protects older deployments and cached links. Organization media URLs and previously published aliases remain available after a site changes its preferred host. Builds use the new preferred addresses; images are not moved into GitHub.

Prepare a domain change before switching traffic

Section titled “Prepare a domain change before switching traffic”
  1. Save the future website and media addresses. This records intent and keeps the current website address active.
  2. Shortly before changing DNS, select Publish for the new domain. This replaces main at the current address with the last successful public snapshot and the installed renderer, excluding newer saved CMS edits. Internal absolute links on the old website origin, canonicals, sitemap addresses and media references are retargeted.
  3. Review Domain verification. Apply any validation records shown if you manage DNS externally. For Cloudflare DNS, use your domain → DNS → Records. Refresh verification or wait for its automatic update.
  4. When publication and certificate checks permit it, select Review traffic switch, then Approve and switch traffic. No second build or upload runs. With external DNS management, apply the reviewed CNAME yourself. Version custom domains require a proxied Cloudflare CNAME.
  5. Typeroll checks the public address before marking it live.

Cloudflare Pages may require traffic DNS to validate a hostname. Typeroll does not overwrite an existing destination while its certificate is unverified, unless you accept a validation window (below). Reviewed DNS changes also stop if another administrator changed the records in the meantime.

Redirect hosts are prepared, approved and switched with the website host. The approval also freezes their reviewed records. At the switch, Typeroll makes sure each redirect rule exists before it changes any record, switches the website host, then makes each redirect record proxied. The deployment is marked live only after every redirect host answers 301 with the same path and query on the website host.

Cloudflare Pages validates a hostname that already serves another website only after its record points at Pages. Cloudflare reports CNAME record not set and offers no way to validate the hostname in advance, so Typeroll reports domain_prevalidation_unavailable. The switch has a window in which the hostname does not answer until Cloudflare finishes validation. Cloudflare does not document how long that takes; plan for a quiet period. There is no zero-downtime method for this case.

Recommended order, for example for example.com with www.example.com redirecting to it, moving from another host:

  1. Add example.com in the Cloudflare account of the site’s Hosting Group. Keep the existing A records for example.com and www pointing at the current host, set to DNS only.
  2. Change the nameservers at the registrar to the two Cloudflare assigned. The zone stays pending until Cloudflare sees them; the website keeps working from the current host because the records are unchanged.
  3. When the zone is active, wait until SSL/TLS → Edge Certificates shows the Universal SSL certificate as active. Cloudflare issues it 15 minutes to 24 hours after activation, and keeps it unused while records are DNS only.
  4. Publish the site once on its generated site address if it has never been published, so the change can be prepared and approved. Save example.com as the website host and www.example.com as a redirect host. Resolve any blockers shown, then prepare the domain change. Typeroll builds the candidate with the new addresses and, with automatic DNS, adds the www redirect rule, which does nothing while www is DNS only.
  5. At a quiet time, select Switch website traffic and accept the validation window. With one reviewed record, Typeroll replaces it with the proxied CNAME to Pages after the final build is verified, makes www proxied, waits for Cloudflare’s validation and checks the public site and the www redirect.
  6. If the site is not live when the 45-minute observation window ends, restore the previous records listed in the message (previous_records in the API).

With several address records on the hostname, or with external DNS management, replace them with the proxied CNAME from the requirements yourself or through your AI agent at a quiet time, then prepare again. Typeroll waits for validation and asks for approval before it marks the change live.

After R2 and the organization shared media host are connected, Typeroll copies old originals to your private bucket, verifies SHA-256 hashes, and changes storage references only after verification. The transfer starts automatically and continues in the background, including libraries with thousands of images. You can close the browser. Publishing → Media storage shows verified copies and updates automatically; temporary interruptions are retried without starting over. Concurrent edits and uploads granted before connection are handled without switching them to an unverified destination. Progress and retry instructions appear in Publishing. Existing source files are retained while older publications may still need them.

From Core 0.2.137 each site migrates on its own. An upload, import or migration on one site never reads, copies or rebuilds media of another site in the Organization. Only the first connection of organization media storage gives every site its own job; a site whose files are already in place finishes without copying and without a preparation build. Changing domains starts no site work: a new media host is only attached to your public bucket. Saving new storage keys or choosing retry restarts only sites whose migration stopped. Publishing → Media storage shows the totals over all sites.

A file whose source cannot be fetched, for example an image on a host that no longer exists, does not hold back the rest of its site. After three attempts, or at once when the source refuses it, the file is set aside with its reason and the site’s job ends as needing attention. read_organization_media_migration (or GET /publishing/media-migration) then lists it under failed_media with its site, filename, source URL and error, and shows each site’s state and updated_at. Delete or replace those files, or fix their source, then retry: a retry gives set-aside files new attempts and restarts only sites that stopped. Set-aside files are never published from the old source, and live pages are not changed.

Original transfer and publication preparation are separate steps. The background transfer moves the library into private R2 storage. A publication prepares only media referenced by its frozen content and any retained published versions.

Core 0.1.94 includes background preparation after uploads and imports, using the Organization’s selected Cloudflare or GitHub build engine. Media shows the progress and offers Retry preparation if a job stops. Setup can happen later: you can upload, import and edit before connecting a build provider. Preparation waits until Publishing → Media storage and Publishing → Builds are ready. Update an existing shared engine after upgrading Core to enable this feature.

Prepared drafts stay in the private R2 bucket. They are not published just because an import finishes. A publication copies only the required original files and variants into its public output. Preparation uses image checksums and a versioned encoding recipe so compatible builds and branches reuse verified variants. From Core 0.2.74, variants made by an earlier release of the image encoder are reused too: each one is checked against its own SHA-256, and only a new recipe version encodes images again. Core 0.2.72 and 0.2.73 rejected them, so the first publication of a site with already-published images failed while preparing media. Changing a website or media hostname does not require encoding the same image again. Republishing does not add a second preparation entry for an unchanged current file. From Core 0.2.17, automatic upload preparation queues the finalized image by ID; it does not scan older images just because they lack preparation status. This applies to browser, API and migration uploads through the same finalization path. Explicit library preparation and already queued library jobs retain their scope. Uploads arriving during a running batch are retained for the next batch. Older media paths and aliases remain available when they are still needed. Verified cached variants are reused without new encoding or storage writes.

Transfers have per-file progress and bounded concurrency. A failed copy does not discard successful copies. WordPress imports reuse a persistent source-address lookup; requests slow down when the source responds with rate-limit or temporary server errors. Original URLs and existing media aliases are preserved.

A shared build can prepare several batches in one provider run, saving a durable checkpoint after each batch. It yields before the provider runtime deadline; another run resumes unfinished work. Image encoding has a separate concurrency limit from file transfers. The existing website stays live until the new publication passes verification.

Waiting publications take priority over background image preparation when a Cloudflare build slot becomes available. An active preparation run finishes its bounded work first; your Cloudflare account’s concurrency limit still applies.

Once preparation is complete, the final build reads the required original and variant files without repeating storage migration or encoding. After a long preparation run, it continues in a fresh build with time to finish and upload.

Finished static files are uploaded directly from the trusted customer build supervisor to Cloudflare Pages with a temporary asset-upload grant. Typeroll receives a bounded manifest and checksums, finalizes the deployment for the frozen Site and branch, and checks that the correct publication is reachable on its domain. Hosting credentials are never passed to the site’s renderer.

From Core 0.1.94, the organization’s build engine also verifies new and changed static files on the deployment candidate. This is a separate job with saved progress, so an interrupted verification resumes without rebuilding the site. Publishing → Builds shows Verifying static files and the checked count. Previously verified files can be reused when their checksums, hosting account, project, domains and control files are unchanged. A text edit does not require re-downloading every unchanged image for verification.

Typeroll performs bounded checks of publication identity and relevant public responses, including small page, asset and removed-route samples. These checks are not an exhaustive second download of the site. The existing site remains available until verification completes. Existing engines must be updated before starting new shared publications; already frozen older jobs retain their original artifact and verification path.

Preparation has a six-hour overall limit, and public verification has its own 45-minute window after the build completes. Direct static output is limited to 512 MiB, 20,000 files and 25 MiB per file. Older binary-artifact engines retain their 128 MiB package limit. Images and generated variants remain outside Git. Corruption, conflicting immutable paths and access errors stop publication; retry after fixing the reported problem. Verified preparation is retained.

For an existing organization, after upgrading Core, go to Publishing → Builds and select Update build engine when no build is running. From Core 0.2.74 the Builds card says Build engine update available when the engine runs older generated code than the installed Core; publishing keeps working until you update. Wait for verification before publishing again. Updating the engine does not require reconnecting GitHub or Cloudflare when the existing connections still have the required permissions. Independent npm run build runs reuse the same verified media; their process scheduling and time limits belong to your own build environment.

New browser uploads go directly to your R2 account after it is ready. Disconnecting that account does not silently move uploads back to the hosting operator.

From Core 0.2.16, public verification distinguishes removed content from retired immutable code bundles. Cloudflare Pages may retain old cached assets for up to one week. A retired versioned Extension CSS/JavaScript file or hashed Astro CSS/JavaScript bundle does not block publication merely because its old public URL still responds. Its absence is still checked on the exact new deployment. All current files remain verified. Removed Pages, media, and unversioned files must still disappear; a fresh publication header alone is not proof.

The next Core release extends this distinction to public, content-hashed derived data at /_typeroll/derived/<sha256>.json. An older dataset may remain cached while the new artifact contains a verified replacement. The replacement bytes and their hash still have to match, and the old file must be absent from the exact new deployment. Removing the dataset entirely retains the removal check. This is not a secure-erasure guarantee: only public data belongs in these files, and cached copies from earlier publications may remain accessible. Private or revocable data belongs behind an authenticated runtime.

This also applies when the updated server resumes a pending publication. No account reconnection, manual status override, or site content change is needed. See Cloudflare’s asset retention behavior.

From Core 0.2.119, a static publication preserves an older media URL when its file cannot safely be retained and the same media record has a safe replacement. For example, sanitizing an SVG changes its bytes and content-hashed filename. Typeroll generates a 301 from the old path to the sanitized file instead of retaining the unsanitized bytes or requiring a broken link. This redirect remains in later publications while that media record is still included. It counts toward the normal Pages redirect limits; dry runs validate those limits too.

The build verifies the replacement’s actual bytes through the old URL. A fresh publication header or a cache-busting query string does not prove the old URL is safe. A conflicting authored redirect fails with media_redirect_conflict rather than being silently overwritten. Media without a safe replacement keeps its normal removal check. This automatic redirect applies to media delivered by the site’s static output; a separate R2 media host has its own routing.

From Core 0.2.15, generated publication builds automatically reuse unchanged pages when a valid previous render cache is available. A normal body-text edit can rebuild that page while reusing other pages. You still publish a complete static website; visitors never receive a mixture of deployments.

Listing and reference blocks record the queries they actually use: filters, selected IDs, ordering, limits and result membership. On the next build these queries run against the current content, including previously empty results. Only visited records contribute value dependencies, so a body edit on page 1 of an archive does not invalidate page 2. Adding, removing, moving, renaming or changing the status of a Page updates affected lists, references, backlinks, breadcrumbs and previous/next links without invalidating unrelated Pages.

Unchanged addresses are excluded from Astro’s HTML generation queue. Their verified HTML is restored into the new output directory; removed addresses are never restored. Shared settings, templates, domains, Core source and Node versions still invalidate reuse. From Core 0.2.17, custom block templates, aliases and containers use the same recorded queries and navigation dependencies as native blocks. Installing a custom block does not make every Page depend on all other Pages. Changing its definition still invalidates shared output.

Image metadata is tracked by the URLs each rendered page actually uses, including lookups that currently find no image. Adding or replacing an image rebuilds its consumers; unrelated pages can reuse HTML. Changing dimensions or variants and removing an image also invalidate its consumers. Within a query, changes to a visited record may cause a rebuild even if the changed field is not displayed. This is dependency tracking, not a promise of minimum possible work.

The route inventory, sitemap, robots rules, redirects, search index and shared asset processing are regenerated from the complete current site. Removed routes are never copied from the cache. Image preparation and verified variant reuse remain a separate process; partial HTML rendering does not eliminate dependency installation, media transfer, static upload or provider startup time.

Core 0.2.18 adds reuse of unchanged media files already available in the target Pages project. Updated shared engines compare the current image completion records with a completed upload receipt, then check Cloudflare’s stored asset hashes before omitting downloads from R2. They combine reused media and newly generated files into one complete deployment manifest. Files no longer referenced by the publication are excluded; retained media aliases remain available when included in the current publication.

Reuse is scoped to the Organization, Site, version, hosting account and Pages project. Availability is checked again after rendering; missing assets trigger normal downloads and another render before upload. An expired or invalid receipt also causes ordinary downloads. The first build with the updated engine creates the receipt for later builds. Build logs include media_report with reused file and byte counts plus materialization and rendering times. These are separate from job.render_report, which counts HTML routes. Provider startup, dependency installation and publication verification still take time; the report does not promise a fixed end-to-end speedup.

For Cloudflare or GitHub shared builds, update the organization build engine in Publishing → Builds after upgrading Core. Both providers use the same renderer and private R2 cache, isolated per Site and version. Only completed build attempts can supply a baseline. Cache files expire after seven days; missing, expired or corrupt files cause a normal full build. Core 0.2.17 introduces media dependency receipts; its first build refreshes an older cache with a full render. Cache storage is optional and never a prerequisite for publishing. Older engines continue building in full until updated.

Independent builds of the generated repository retain .publication-cache.json beside publication.json between runs. Keep it outside Git and the public dist/ directory. The current cache is limited to 32 MiB; a larger cache is discarded. Use TYPEROLL_FULL_BUILD=1 npm run build to compare against a clean full render. Ordinary starter builds outside the frozen-publication workflow do not use this cache automatically.

The editor’s deployment status reports pages rebuilt and reused after rendering. API and MCP clients read job.render_report through get_deploy_status; mode, rendered, reused, total, removed and reason describe actual HTML work. Counts include generated pagination and facet routes. get_publication_impact remains a provisional source comparison, not a prediction of cache hits or monetary savings.

The generated repository includes public content, renderer source and a dependency lock. Run npm ci and npm run build with its pinned Node version to build it on your own infrastructure. R2 originals remain separate. Cloudflare builds receive short-lived grants for the exact original and output objects. For independent builds, provide your own R2 access as described in the generated README.

Forms, Apps and Extensions can still depend on their runtime services; the repository lists these in publication.json under runtime_dependencies. Starting with Core 0.1.81, generated source preserves browser API keys in Extension configuration fields explicitly declared public by the published Extension schema. These values are visible in the website and its repository. Use browser credentials restricted to the intended websites and APIs; keep server credentials in secret configuration fields. Private keys and GitHub tokens are rejected even in public configuration. Fields that are private, secret or absent from the published schema are excluded from the runtime.

Content is committed as one file per record under content/, with sorted keys and indentation. A text change produces a readable field-level diff. The first publication using this format rewrites the existing compact JSON files once.

A generated site repository is not a full CMS backup. It includes published public content, not drafts, unpublished versions, form delivery actions, private or secret Extension configuration, or image originals.

Future hosts and the publication destination

Section titled “Future hosts and the publication destination”

Core 0.2.116 / MCP 0.45.87 (pending release): saving future domains is inert. Normal publication uses the current embedded origins until you explicitly choose Publish for the new domain (prepare_publishing_domain_change). That action rebuilds/retargets the last published snapshot and replaces main, including at the current/temporary address. It excludes later unpublished CMS edits.

The domain read separates desired (saved intent), published.hosts (addresses embedded in main) and active (verified traffic address). They are configuration states for one served main artifact, not parallel site versions.

  1. Finish and publish intended content normally; verify it at the current address.
  2. Save future website, media and redirect hosts (this can be done earlier).
  3. Shortly before DNS changes, explicitly publish for the new domain.
  4. Review the latest main artifact, indexing settings, certificates and DNS records.
  5. Approve the DNS switch separately. The same commit and files remain deployed.

Absolute links and media URLs on the future hostname may not work until DNS and certificate activation finish. The temporary hostname now serves the new artifact; it is not a retained copy of the old site. Media aliases are preserved.

You can publish further changes before switching DNS. They retain the embedded origins and refresh the DNS approval receipt automatically. No repeated domain preparation is required. An approval referring to an older main publication is rejected. While a DNS switch is actively running, publications wait for it.

To correct indexing, save the setting and publish normally. Review the latest receipt before approval. A separate preview-branch candidate from an older Core release must be replaced once with Publish for the new domain. DNS rollback restores records only; it does not restore an old artifact or its embedded origins.

Site API keys need admin permission for domain writes. Organization settings require an organization API key. An external agent can use its own Cloudflare access to configure DNS and R2 rules, then ask Typeroll to verify them.

Action MCP tool API route below /api/v1
Check setup read_publishing_readiness GET /sites/{site}/publishing
Read GitHub/Cloudflare connections read_organization_publishing_connections GET /publishing/connections
Explain the GitHub connection diagnose_organization_github_connection GET /publishing/github-diagnosis
Explain a Cloudflare connection diagnose_organization_cloudflare_connection GET /publishing/cloudflare-diagnosis
Read Analytics for every Site list_organization_analytics GET /organization/analytics
Turn Analytics on for chosen Sites set_organization_analytics POST /organization/analytics
Disconnect GitHub or Cloudflare disconnect_organization_publishing_provider DELETE /publishing/connections/{provider}
Connect Cloudflare with a token connect_organization_cloudflare POST /publishing/connections/cloudflare
Prepare media buckets prepare_organization_media_storage POST /publishing/connections/cloudflare
Save R2 media keys save_organization_media_access POST /publishing/connections/cloudflare
Read site hosts read_publishing_domains GET /sites/{site}/publishing/domains
Save future hosts set_publishing_domains PUT /sites/{site}/publishing/domains
Prepare last public snapshot prepare_publishing_domain_change POST /sites/{site}/publishing/prepare
Approve verified traffic switch approve_publishing_domain_cutover POST /sites/{site}/publishing/cutover
Read organization domain read_organization_publishing_domains GET /publishing/domains
Save organization domain set_organization_publishing_domains PUT /publishing/domains
Read media migration read_organization_media_migration GET /publishing/media-migration
Retry media migration retry_organization_media_migration POST /publishing/media-migration
Read a private image temporarily read_private_media_url GET /sites/{site}/media/{media}/content
Revoke a preview revoke_preview_link POST /sites/{site}/preview-link/revoke

Before enabling Analytics or saving redirect hosts, an agent reads cloudflare_permissions from read_organization_publishing_connections (one entry per Hosting Group) or permissions from diagnose_organization_cloudflare_connection. Each capability, analytics_provisioning, analytics_reports and redirect_rules, has a status: available, approval_required, not_offered (this installation does not request the scope), unknown (API token connection, checked when used) or connection_required, plus missing_scopes and next_step. permission_update names the missing scopes, the Hosting Groups one approval renews and its connect_url, where a person selects Approve update.

Connection changes use the revision from the connections status. Signing in to GitHub, installing the GitHub App and Cloudflare OAuth sign-in need a person in a browser: the status returns connect_urls, links to the provider cards on the Publishing page, where an organization owner or admin completes the step. Everything after that — Cloudflare with a customer API token, media buckets, R2 keys, builds, domains and disconnecting — works through the API and MCP. diagnose_organization_github_connection explains why GitHub is not connected and who must act, with one link or browser step per blocker; relay it to the person who completes the step. diagnose_organization_cloudflare_connection does the same for the Cloudflare connection of the Organization or a Hosting Group (hosting_group_id). POST /publishing/connections/github answers 409 with connect_url and the same diagnosis. The connection routes need an organization API key, matching the owner/admin rule of the Publishing page; a site key gets 403 there and uses the site routes above.

Domain writes include the current revision. Cutover also includes candidate_id. A stale revision returns 409. Use the normal deploy-status API to inspect build progress and read_publishing_domains for structured DNS requirements.

Site domain settings accept redirect_hosts, a list of hostnames. Reads and saves return host_checks with blockers and warnings, each with code, host and message: for example apex_zone_required, apex_zone_in_other_account, zone_not_active (with the zone’s nameservers), redirect_zone_required or redirect_host_certificate_unavailable. Preparing a domain change returns 409 with the first blocker’s code until they are resolved. After preparation, redirect_preparations holds each redirect host’s record, its Single Redirect rule (expression, target_url_expression, status, managed) and certificate state. An agent with its own Cloudflare access can create the rule and record, then prepare again; Typeroll verifies the public 301. When preparation.validation_blocker.resolution is accept_validation_window, approve with accept_validation_window: true only after the person responsible for the website agrees to the window.

Starting with Core 0.2.20, an authenticated build or verification completion immediately queues the next publication step. This works with both organization build providers. The portal verifies that the result still belongs to the same site version and frozen publication; an older or cancelled build cannot publish newer edits.

Periodic observation remains a recovery mechanism if the completion signal or queue is temporarily unavailable. A finished build still needs static hosting and public availability checks before its link is shown as live. Immediate continuation reduces avoidable waiting; it does not skip these checks or eliminate the build provider’s queue time. Self-hosted installations use their configured deploy queue and must keep its worker running.

From Core 0.2.21, publication steps are chained through saved events. Completing the customer build immediately continues the same publication. Slow builds do not cause a second build to start. Duplicate result messages are safe to deliver.

A reported build failure, or loss of the build engine’s execution heartbeat, appears in the portal and deployment-status API/MCP response. From Core 0.2.74 the error names the step that failed, why, and the line the step printed as its cause, for example: The build failed while preparing images and files: the step exited with code 1. Cause: Error: Invalid media completion receipt. The deploy job’s failure.code keeps the reported code (media_build_process_exit_1) and failure.diagnostic keeps the cause and the last 40 lines the step printed, with values that look like credentials removed. The portal shows them under Build output. Both the Cloudflare and the GitHub engine report this once they are updated; the engine also writes the same lines to its own build log, prefixed TYPEROLL_BUILD_LOG and TYPEROLL_BUILD_CAUSE. For a build on the Cloudflare build engine (Core 0.2.73), Typeroll reads that build’s log with the organization’s build connection and adds the most specific failure line to the error, for example Build log: Error: Cannot find module …. The deploy job’s failure.provider_log keeps the last 60 lines, whether the build or the verification of its output failed, and the Cloudflare build ID. Values that look like credentials are removed; digests and commit IDs are kept. Resolve the cause, then retry publishing. A status refresh does not start a replacement build. Brief distribution checks can continue after upload until the exact static files are publicly available; the live link remains hidden until verification succeeds.

The deployment-status response includes verification_message and static_probe when a public file still has the wrong status or content. Check the observation’s checked_at, route, expected/actual status and Cloudflare cf_ray before assuming the displayed result is old. A matching publication marker alone does not prove that every removed page already returns 404.

From Core 0.2.108: failed probes additionally report final_path, a same-origin redirect_chain, selected response_headers and body_sample. The sample contains a SHA-256 fingerprint of at most the first 4 KiB and its byte count, never response text. scope distinguishes a complete body, a bounded prefix and an interrupted partial sample. Compare fingerprints only with the same byte count and scope. Query parameters, fragments, cookies and arbitrary response headers are excluded. A diagnostic fingerprint is evidence for troubleshooting, not a replacement for the artifact’s full content check.

From Core 0.2.126: static_probe.reason names the actual cause. status_mismatch and content_mismatch are evidence about the deployment. timeout and connection_failed mean the bytes were never fully read; they are retried twice within the same check, and attempts and a non-secret error_code (for example ECONNRESET) show what happened. A redirect that leaves the site, has no destination or exceeds three hops is reported as redirect_offsite, redirect_without_location or redirect_limit. request_failed remains only for unclassified errors. A body longer than its file stops at that file’s size and is reported as content_mismatch on that route, so a shared byte limit never blames a different file. A matching status and publication marker are not enough: every probed file must also have the published bytes.

Different responses from different hosting locations are evidence to investigate; they do not by themselves establish a provider bug. Keep the same deployment ID while investigating. A local observer timing out does not cancel the server job or authorize another publication. If server verification eventually times out, use Retry verification only after resolving the reported problem; it retains the same frozen build.

Core 0.2.117 (pending release): after the candidate artifact and public publication marker are verified, Typeroll detects cached responses that still contain changed or removed files. It refreshes the affected URL prefix and checks the ordinary public URL again. Attempts are bounded (at most three affected prefixes per publication); each refresh allows up to six checks with queue backoff for propagation. No additional build, image processing or upload is started.

If the same stale response remains, the deployment stops with a specific cache error and keeps the exact file path and Cloudflare request ID in static_probe. It does not wait out the full distribution timeout or declare old bytes correct. Cloudflare documents that stale Pages assets may require a full cache purge. Typeroll never performs that broad operation implicitly: a zone can include unrelated sites and subdomains.

An Organization administrator can choose Refresh domain cache… beside the failed deployment and confirm the named zone. This resumes the same publication and verifies its public URLs after the purge. It does not edit files, republish later CMS changes, or change DNS. Site administrators without Organization admin access must ask their Organization administrator. Agents have the same operation through retry_deploy_verification.

If another CDN controls the cache, refresh it there and choose Retry verification. That button does not grant permission for a zone purge. If Cloudflare still returns old bytes after the approved broad refresh, review its cache rules or contact Cloudflare with the recorded request ID, then retry verification. Typeroll cannot guarantee immediate eviction from every provider or from a visitor’s browser.

This recovery applies to completed publications already serving the intended host. It cannot resume an incomplete build or bypass domain traffic approval, a changed hosting connection, or a newer publication. Pre-switch domain preparation retains its existing verification and traffic-approval safeguards.

Core 0.2.22 checks the actual frozen build output before activation. See Publication checks for blocking defects, editorial warnings, engine updates and schema-mapping migration. Both Cloudflare and GitHub builds use the same gate; a failed check keeps the current live site intact.

From Core 0.2.103, a publication request with unsaved Pages or shared blocks returns 409 unsaved_content before a job is queued. Save or discard the listed working copies first. Alternatively, explicitly confirm publish saved content only in the portal, or pass allow_unsaved: true to trigger_deploy / the deploy API. This never saves drafts or another editor’s work. It applies to the selected version and also to dry runs, which validate saved content.

Deployment status can include build_progress: the exact task’s status, attempt, step and completed/total files. These counters are not an ETA or a whole-job percentage. Existing status polling reads progress; no additional scheduled job is started. A new attempt does not mean that completed media must be recopied.

get_preview_link also returns frame_url for rendering QA without the portal wrapper. It uses the same signed expiry and sandbox. Keep the normal url for extension-host integration tests, which need the wrapper’s message bridge.

Media transfer retries and failure details

Section titled “Media transfer retries and failure details”

From Core 0.2.109, the frozen customer build retries a temporary media transfer up to three attempts for the same operation. This includes fetching scoped access, reading completion/variant receipts, reading an interrupted response body and writing immutable assets. Completed variants and reusable publication assets are kept; a retry does not deliberately regenerate unchanged images. The coordinator’s existing bounded checkpoint recovery still applies after local transfer attempts are exhausted.

A final media error records its operation, media ID and file locator in build logs; the portal/MCP diagnostic includes the bounded locator beside the cause. Signed storage URLs and credentials are not included in these locators. A publication-wide access failure may have no individual media ID yet. Access denials, invalid receipts, changed hashes and image-encoding errors are not transient transfer failures and are not automatically retried locally.

These changes apply to new publications frozen with this Core release. A job already frozen with an older release retains its original media code; retrying that exact snapshot does not upgrade it. If a failure remains after retries, report the deployment ID and diagnostic. A failed build does not activate a new live artifact.

Core 0.2.114 distinguishes waiting_for_runner from preparing_media in build progress. An unclaimed step expires after 15 minutes, including a media continuation waiting for its next runner; completed media is retained. The deadline is enforced when the runner claims it or its status is inspected. Expiration revokes the task; it does not schedule a duplicate build or imply that Cloudflare cancelled its underlying process. A healthy active lease is not timed out because the overall media job is old. Check Publishing → Builds, then retry after the terminal error. A new publication does not automatically replace an active one. GitHub 422 errors include a safe classified reason when available, never the raw provider response.

The overview distinguishes saved content from the latest publication result. A failed final check shows Publication needs attention, even if the website is already reachable. It does not claim the site is up to date until verification succeeds. A brief explanation and recovery action remain visible; Technical details expands the original diagnostic, including the affected path and deployment ID. Retry verification checks the existing publication without rebuilding it.

After a domain switch, the old website address should redirect to the new one with the same path and query string. Publishing lists Previous website addresses and their verified redirect or noindex status. If Cloudflare denies Single Redirect access, the old address remains protected by a verified noindex header and Publishing explains what still needs attention. Existing media paths remain available; a media hostname is not removed simply because the preferred website address changes. See Previous website addresses for permissions, verification and rollback behavior.