---
name: publish-to-mylocalhost
description: Publish a user-owned static HTML page or ZIP website to mylocalhost.ai and return a shareable URL. Use when the user asks to upload, deploy, host, publish, or share a static website through mylocalhost.ai.
---

# Publish to mylocalhost.ai

Use the public deployment API to publish static sites without an account.

Canonical skill URL: `https://mylocalhost.ai/SKILL.md`

## When to use

Use this skill when the user asks to publish a site they own or are authorized to publish and the source is one of:

- an HTML string
- one `.html` or `.htm` file
- one `.zip` archive containing a static website

Do not upload third-party content without authorization. Do not upload secrets, private keys, credentials, databases, server-side code, or personal data.

## API

Endpoint:

```text
POST https://mylocalhost.ai/api/v1/deployments
Content-Type: multipart/form-data
```

Required fields:

- `source_type`: `html_text`, `html_file`, or `zip`
- `public_confirmed`: `true`; this confirms both public URL publication and eligibility for the approved site, thumbnail, and AI-generated description to appear in the public gallery
- exactly one source:
  - `html_text` for `html_text`
  - `file` for `html_file` or `zip`

Optional fields:

- `title`: up to 200 characters
- `slug`: 8–64 lowercase letters, numbers, or hyphens; no underscores; must start and end with a letter or number
- `creator_name`: optional public creator display name, up to 100 characters
- `source_application_name`: the AI tool used to create or publish the page, such as Claude Code, Codex, or Alice; this is not the uploaded website's name
- `source_application_url`: optional public HTTPS website for a newly submitted source application
- `source_model_name`: the large language model that wrote the page, such as `Claude Opus 4.8`, `Kimi K3`, or `GPT-5.6 Sol`; one model per upload, used only for the public model benchmark. Read "Reporting the model" below before filling this in
- `source_application_descriptions`: optional JSON object containing any subset of `de`, `en`, `es`, `fr`, `ja`, `ko`, `pt`, `ru`, `zh-CN`, and `zh-TW`; missing languages are generated asynchronously and never delay publication

An explicit user request to publish their content counts as confirmation. If ownership or public visibility is unclear, ask before sending the request.

## Public gallery authorization

`public_confirmed=true` has one consistent meaning across the website and API:

1. The resulting site URL is public.
2. After moderation passes and the thumbnail and site profile are ready, the deployment is automatically eligible for the homepage and `/gallery`.
3. Gallery eligibility does not guarantee a fixed position or ranking.
4. The management-key holder can opt out later without deleting or unpublishing the site.

Do not send `public_confirmed=true` merely because an AI generated the files. Confirm that the user asked to publish or otherwise clearly authorized public visibility and gallery eligibility.

## Reporting the model

The benchmark ranks models by how the public receives the pages they produce, so one set of weights must arrive under one name. If you are the assistant that wrote the page, report your own model. If another model wrote it, report that one. Report the model that generated the page's code, not a model that only ran the upload command.

Send the published brand name, not the API identifier your runtime exposes:

| Your runtime reports | Send |
| --- | --- |
| `anthropic/claude-opus-4.8-20260214` | `Claude Opus 4.8` |
| `qwen3.6-plus-preview` | `Qwen 3.6 Plus` |
| `gpt-5.6-sol:free` | `GPT-5.6 Sol` |
| `moonshotai/kimi-k3` | `Kimi K3` |

Four rules produce that name:

1. Drop the vendor prefix and any routing path, so `anthropic/` and `openrouter/` disappear.
2. Drop access tiers and rollout stages: `free`, `preview`, `beta`, `latest`, `stable`, `(free)`, `:free`. A preview or free tier is the same model as its base and belongs in the same row.
3. Drop dated snapshots such as `-20260214` or `-2026-02-14`.
4. Keep the version number exactly as the vendor publishes it. `Claude Opus 4.8` and `Claude Opus 4.7` are different models and must not be merged.

To be exact, fetch `GET https://mylocalhost.ai/api/v1/models` first and send the matching entry's `name` verbatim. The API also accepts these forms directly and folds them onto the base model, but sending the catalog name avoids any ambiguity.

Omit `source_model_name` entirely when you do not know which model wrote the page. Never guess a model, never report a model family without its version, and never report a product or agent name such as `Claude Code` or `Cursor` here; those belong in `source_application_name`.

## Upload

Pasted HTML:

```bash
curl --fail-with-body -X POST "https://mylocalhost.ai/api/v1/deployments" \
  -F "source_type=html_text" \
  -F "html_text=<index.html" \
  -F "title=My site" \
  -F "public_confirmed=true"
```

Write the markup to a file first and keep the `<index.html` form, which tells curl to read the field value from that file. Inlining the markup into `-F` fails with `curl: (26)`, because curl reads a value that starts with `<` as a filename.

Single HTML file:

```bash
curl --fail-with-body -X POST "https://mylocalhost.ai/api/v1/deployments" \
  -F "source_type=html_file" \
  -F "file=@index.html;type=text/html" \
  -F "title=My site" \
  -F "public_confirmed=true"
```

ZIP website:

```bash
curl --fail-with-body -X POST "https://mylocalhost.ai/api/v1/deployments" \
  -F "source_type=zip" \
  -F "file=@site.zip;type=application/zip" \
  -F "title=My site" \
  -F "public_confirmed=true"
```

Add `-F "slug=my-static-site"` only when the user requests a custom slug.
When the source tool is known, add `-F "source_application_name=Claude Code"`.
When the model is known, add `-F "source_model_name=Claude Opus 4.8"` using the brand name rules in "Reporting the model".

The accepted response includes `application_resolution`:

- `matched`: a unique existing source application was linked
- `provisional`: a new pending application was registered
- `needs_selection`: show `application_candidates` to the user and ask which one is correct
- `unnamed`: no source was supplied
- `hidden`: the submitted source name failed metadata review; this does not by itself block the page

`model_resolution` follows the same states for `source_model_name`. A vendor prefix, access tier, or dated snapshot still resolves to `matched` against the base model. A name that matches nothing becomes a pending catalog entry and stays out of the public benchmark until an administrator verifies it or a published work backs it, so check `model_resolution` in the response: `provisional` means your name did not match the catalog and is worth re-checking against `GET /api/v1/models`.

Use the management key to resolve a candidate or correct only this deployment:

```text
PATCH https://mylocalhost.ai/api/v1/deployments/<deployment_id>/application
Authorization: Bearer <manage_token>
Content-Type: application/json

{"application_id":"app_..."}
```

Correct the declared model the same way:

```text
PATCH https://mylocalhost.ai/api/v1/deployments/<deployment_id>/model
Authorization: Bearer <manage_token>
Content-Type: application/json

{"model_id":"mdl_..."}
```

`GET https://mylocalhost.ai/api/v1/models` returns the catalog with each entry's `model_id`, `slug`, provider, and current benchmark position.

Application records are administrator-curated. Do not claim application ownership. Requests to edit an application name, icon, canonical link, aliases, or official descriptions go to `connect@mylocalhost.ai`.

## Protect the management key

The response contains:

```json
{
  "deployment_id": "...",
  "status": "processing",
  "status_url": "...",
  "manage_token": "...",
  "expires_at": "..."
}
```

The `manage_token` is shown once and cannot be recovered. Treat it as a secret:

1. Never print it in chat, logs, commits, issue trackers, or public files.
2. Save the complete response to a user-approved local secret file, such as `.mylocalhost/<deployment_id>.json`.
3. Restrict the file to the current user (`chmod 600` where supported).
4. Ensure `.mylocalhost/` is excluded from version control.
5. Tell the user where the key was saved.

If secure storage fails, stop and warn the user before losing the response.

## Update an existing deployment

The downloaded `.mylocalhost-key` v2 file contains `skill_url`,
`source_update_endpoint`, `status_endpoint`, and the secret `manage_token`.
When the user gives this file to an AI together with revised HTML or ZIP:

1. Read this Skill.
2. Keep `manage_token` secret.
3. Send the revised source as multipart data to `source_update_endpoint` using
   `Authorization: Bearer <manage_token>`.
4. Poll `status_endpoint` until moderation completes.

```bash
curl --fail-with-body -X PUT "<source_update_endpoint>" \
  -H "Authorization: Bearer <manage_token>" \
  -F "source_type=zip" \
  -F "file=@site.zip;type=application/zip"
```

Replacing a source keeps the stable deployment URL but removes the previous
published revision while the new revision is scanned, rendered, and moderated.
Never imply that possession of the key bypasses review.

## Leave or rejoin the public gallery

Use the management key to withdraw a ready deployment from the gallery while
keeping its public URL online:

```text
PATCH https://mylocalhost.ai/api/v1/deployments/<deployment_id>/showcase
Authorization: Bearer <manage_token>
Content-Type: application/json

{"requested":false}
```

Send `{"requested":true}` to make it eligible again. Explain that the site
still needs `ready` status, a thumbnail, and a completed site profile before it
can appear.

## Wait for publication

Poll the private status endpoint with the management key:

```text
GET https://mylocalhost.ai/api/v1/deployments/<deployment_id>/private
Authorization: Bearer <manage_token>
```

Poll every 2 seconds initially, then back off to 10 seconds. Stop after 10 minutes unless the user asks to continue.

Terminal outcomes:

- `ready`: return `site_url` to the user
- `review`: explain that moderation is pending
- `blocked`: explain that publication was rejected
- `failed`: report the safe error details and suggest correcting the package
- `deleted` or `expired`: explain that the deployment is unavailable

Do not invent a public URL before `site_url` is returned.

## Delete

Only delete after explicit user confirmation:

```text
DELETE https://mylocalhost.ai/api/v1/deployments/<deployment_id>
Authorization: Bearer <manage_token>
```

## Limits

- pasted HTML: 5 MiB
- HTML file: 20 MiB
- ZIP upload: 100 MiB compressed
- extracted ZIP: 200 MiB, 500 files, 50 MiB per file
- ZIP may contain static assets only: HTML, CSS, JS, JSON, images, fonts, audio (MP3/WAV/OGG/M4A/AAC/FLAC/Opus), video (MP4/WebM)
- sites are scanned, rendered, and moderated before publication
- anonymous deployments expire according to the `expires_at` value

## Final response

Return:

- deployment status
- shareable `site_url` when ready
- whether the deployment is eligible for the public gallery
- expiry time
- local path where the management key was saved

Never include the plaintext management key.
