Publish a static site #

View this page as Markdown

Prompt:

Publish this HTML page, stylesheet and image as a public site.

Put the public files under client/ and include a small Node.js server for the health check. The platform serves the files before requests reach that server. The same server below also serves them when you test on your own computer.

What your AI tool does #

  • Keeps the page, stylesheet and image together under client/, linked by root paths such as /styles.css and /logo.png.
  • Adds package.json, a server and the manifest, then checks the site locally where a shell is available.
  • Chooses an available deploy route, waits for the outcome, and checks the deployed page and image.
  • Asks which files may be public before placing them under client/.

What you need #

  • The HTML page, stylesheet and image you want to publish, with no secret or private file among them.
  • A connected, signed-in AI tool for the deploy (Connect your AI coding tool).
  • Node.js 24 or later for a run on your computer. A tool with no shell can use the file-list deploy route and skip that local run.

Before your AI starts #

These steps are for your AI tool.

  • Read the deploy skill with read_context, id skill:deploy.
  • Ask the account's owner which plan to use when creating the application. Follow Deploy from your computer for application creation and manifest submission.
  • Keep every file under client/ public. This example needs no database, sign-in for visitors, library package or runtime dependency.

Steps #

1. Put the page's files in the deploy folder #

Use this layout, with your own HTML, CSS and PNG image:

package.json
server.mjs
manifest.json
client/index.html
client/styles.css
client/logo.png

In index.html, link the stylesheet as /styles.css and the image as /logo.png. If your image has another name or type, update the HTML and the local server's file table together. Keep files the page does not need outside client/.

Write package.json at the folder's root:

{
  "name": "static-site",
  "private": true,
  "type": "module",
  "scripts": { "start": "node server.mjs" }
}

With no dependencies, this application needs no package lockfile or installation step of its own. The deploy still needs the root package file and its start script.

2. Add the health server and manifest #

Write server.mjs beside package.json:

import { createServer } from 'node:http';
import { readFile } from 'node:fs/promises';

const files = new Map([
  ['/', ['index.html', 'text/html; charset=utf-8']],
  ['/index.html', ['index.html', 'text/html; charset=utf-8']],
  ['/styles.css', ['styles.css', 'text/css; charset=utf-8']],
  ['/logo.png', ['logo.png', 'image/png']],
]);

createServer(async (request, response) => {
  request.signal?.addEventListener('abort', () => response.destroy(), { once: true });
  const path = new URL(request.url, 'http://localhost').pathname;
  if (request.method !== 'GET') {
    response.writeHead(404).end();
    return;
  }
  if (path === '/health') {
    response.writeHead(200, { 'content-type': 'text/plain' }).end('ok');
    return;
  }
  const file = files.get(path);
  if (!file) {
    response.writeHead(404).end();
    return;
  }
  try {
    const bytes = await readFile(new URL(`./client/${file[0]}`, import.meta.url));
    if (!response.destroyed) response.writeHead(200, { 'content-type': file[1] }).end(bytes);
  } catch (error) {
    if (!response.destroyed) response.writeHead(error.code === 'ENOENT' ? 404 : 500).end();
  }
}).listen(Number(process.env.PORT || 8080), '0.0.0.0');

The file table lets a local run serve only the named files. In a deployed copy, the platform normally serves client/ first. While its file store fails, a request for a file beyond the shell can reach your server as an ordinary request (Serve a web client); this server can serve it too. /health reaches this server. The deploy gate needs three consecutive 200 answers within 180 seconds, so keep the server running and answer that path as soon as it listens (The deploy health check).

Write and submit this manifest.json before the first deploy:

{
  "manifest_version": 1,
  "services": [],
  "health": "/health",
  "region": "usa",
  "egress": [],
  "audience": { "kind": "public" },
  "packages": []
}

packages: [] declares that the application uses no library entry. The server uses Node.js's built-in modules alone. Author the manifest explains the other settings.

3. Check it on your computer #

Where a shell is available, start the application from this folder:

npm start

Keep it running while you request http://localhost:8080/health, /, /styles.css and /logo.png; each should return 200. Open the page in a browser when available and check that the image and styles load. If PORT is set, use that port instead. Stop the process you started when the check is finished.

This server reads no platform credentials or service settings, so this local check needs no provision command or Turn Zero sign-in. A tool that cannot run a shell skips the check and reports it as unrun.

4. Deploy the folder and check the result #

Follow Ways to deploy to choose the route. Submit the manifest and call check_deploy as that page directs before deploying.

  • Prefer the turnzero command where it works. It sends the folder and binary image together.
  • For a first small test or a tool with no shell, deploy_files can send the whole file list. Send the PNG's bytes as base64 in content with "encoding": "base64". Its limits are 512 KiB per file, 200 entries and 4 MiB in total; the request's JSON also has a 4 MiB bound. Deploy from a chat explains splitting calls within those limits.
  • Past those limits, use the command or the upload route from Ways to deploy, as the tool permits. Preserve any actual permission refusal.

Read read_status with wait_seconds until the deploy ends. Then request /health, /, /styles.css and /logo.png on the hostname the platform returned. With no shell, use request_route. Read a failed outcome before retrying; a lost response does not show whether the deploy started.

Expected result #

The deploy reaches deployed; /health returns 200 and the page, stylesheet and image load on the application's public hostname. A request the platform serves as a client file wakes no application and costs no backend action. A request falling back to this server uses a backend action. The page, client/index.html, and stylesheet, client/styles.css, are shell files and count nothing. The image, client/logo.png, is beyond the shell and counts its bytes toward data_transfer_bytes (Serve a web client).

Refusals #

What you see Cause Remedy
The deploy's health check fails The server did not listen on PORT or /health did not answer 200. Read the failed outcome and logs; check the start script, port and health route before another deploy.
The image or stylesheet returns 404 A file is missing or the HTML uses another name or case. Match the deployed path and filename, including case; keep each file under client/.
deploy_files_too_large The file list is past that route's limits. Use the command or upload route where available; do not split calls to bypass the total limit.