Contract
What ClaudeToHost accepts
Three shapes of project, and one explicit contract for the two that have to run. Nothing here is a convention you have to guess: if your project does not match, you are told which rule it missed.
A front end
Nothing to declare. Zip the project folder — the one with package.json in it — and the build runs on our side.
We run npm ci (or npm install when there is no lockfile), then npm run build, and serve whatever lands in dist/, out/, build/ or .output/public. It takes about a minute; meanwhile your address already answers, with a page that says so.
The build runs sandboxed, without cluster credentials, and can reach nothing private — npm install executes third-party install scripts, and that is treated as what it is.
Something already built
If you would rather build it yourself, drop the output and we skip straight to serving it.
A single top-level folder is stripped automatically, so zipping dist/ and zipping its contents both work. The rule is simply: a package.json at the root means build, no package.json means serve.
Projects that run: claudetohost.yaml
A service or a worker needs two files at the root of what you drop: a Dockerfile, and this manifest. It declares how to run the thing — and, for env vars, only their NAMES.
Why a worker has no port
A Discord bot or a queue consumer is not a website. Declaring a port on one is rejected rather than ignored: a worker gets a deployment and nothing else — no service, no ingress, no public surface it never asked for.
Why env is names, not values
A mapping of KEY to value is refused outright. Values are supplied later from the site's page and land in a Kubernetes Secret in your own namespace; they never enter the manifest, the repository, or the deployment record.
Dockerfile requirements
- A non-root final
USERis mandatory. Not a suggestion — a build whose lastUSERis root, or absent, is refused. It is also why a web port must be ≥ 1024: a non-root process cannot bind lower. - Multi-stage where it makes sense: build in one stage, copy the artifact into a slim runtime. A single-stage image is a warning, not a refusal.
- Logs to stdout/stderr. Anything written to a file inside the container is invisible and lost on restart.
- No interactive shell as the entrypoint — the image has to start on its own.
- No secrets in the image or the repo. Files are scanned for key-shaped strings and a match fails the upload with the offending line.
ADD <url>is refused: an opaque network fetch at build time is not reproducible.
Limits
- Archive
- 40 MB, 5000 files
- Formats
- .zip · .tar.gz
- Per site
- 1 replica · 1 vCPU · 512 MiB
- Refused outright
- symlinks · paths escaping the archive