kiln 0.3.0 is a Cloudflare-hosted workspace for versioned CadQuery projects, asynchronous CAD builds, immutable artifacts, and bounded geometry preflight. It serves three audiences:
- People author source, parameters, project metadata, and Markdown documents in the browser, start from a CadQuery template, inspect build history and renders, and orbit archived STL files in the built-in 3D viewer.
- Agents use the Streamable HTTP MCP endpoint or the REST API. Public reads do not require credentials; writes and compute use Cloudflare Access Managed OAuth, an Access service token, or the transition API key.
- Operators deploy one Worker, isolated Python engine containers, a Workflow, D1, R2, and Durable Objects from this repository.
Production: https://kiln.timcf.workers.dev
Version 0.3.0 is the deployed release baseline. The current source gate covers 17 Worker/MCP
integration tests, 30 engine tests, TypeScript and browser script checks, the
engine Docker image, a clean migration apply, and a Wrangler deployment dry
run. The pinned dependency tree has no known npm audit vulnerabilities, and
CI rejects moderate-or-higher production dependency advisories.
Deployment verification includes shallow and deep health probes, Access and transition-key write enforcement, MCP initialization and discovery of all 20 tools, and startup of the CadQuery 2.8 engine. See the changelog for the release details.
A kiln verified build means the script completed, the configured bounded
geometry checks passed, and the artifact archive was hash-verified. The checks
are preflight heuristics. They do not establish that a model is printable,
needs no supports, fits a physical mating part, has adequate strength, is safe,
or complies with a manufacturing or regulatory requirement. Inspect the model,
slice it for the actual machine and material, and validate critical dimensions
and loads independently.
All project metadata, source, parameters, documents, build reports, and artifacts are public on the public hostname. Never submit secrets or private designs.
| Surface | Public | Access identity required |
|---|---|---|
| REST | Health, session state, projects, source history, parameters, documents, builds, artifacts | Create or edit data, queue/retry/cancel builds, measure or verify geometry |
| MCP | Read tools, five resource templates, one prompt | Write and compute tools through Managed OAuth |
| Engine | GET /api/engine/healthz |
No other engine route is exposed through the Worker |
For Cloudflare Access, configure these Worker variables to match the Access applications protecting the browser and MCP hostnames:
CF_ACCESS_TEAM_DOMAIN=https://your-team.cloudflareaccess.com
CF_ACCESS_AUD=whole-host-application-audienceThe browser uses the Access session cookie without exposing it to JavaScript.
RFC 8707-capable MCP clients use Managed OAuth. Cloudflare validates the opaque
OAuth token and forwards a signed Cf-Access-Jwt-Assertion; the Worker verifies
that assertion against the team JWKS, issuer, and configured audience.
KILN_API_KEY remains an optional compatibility and local-development fallback.
Send its value in either header:
Authorization: Bearer <key>
X-Kiln-API-Key: <key>If both key headers are sent, they must match. Do not put credentials in a URL, source file, parameter, document, or artifact. See public/auth.md for the browser, Managed OAuth, Access JWT, service-token, and migration flows.
- Browser application: https://kiln.timcf.workers.dev/
- MCP endpoint: https://kiln.timcf.workers.dev/mcp
- MCP Registry metadata: https://kiln.timcf.workers.dev/server.json
- REST guide: https://kiln.timcf.workers.dev/api.md
- Canonical OpenAPI: https://kiln.timcf.workers.dev/.well-known/openapi.json
- API catalog: https://kiln.timcf.workers.dev/.well-known/api-catalog
- Standalone agent skill: https://kiln.timcf.workers.dev/agent-skills/kiln-cad-builds/SKILL.md
MCP exposes 20 tools, five URI resource templates, and the cad-discipline
prompt. Tool results include structured output envelopes, stable errors,
behavior annotations, and artifact resource links. See
public/llms.txt for the compact inventory.
Requirements are Node.js 22.18 or newer, npm, Python 3.12 with the engine requirements, and Docker for local container execution.
npm ci
python3 -m venv .venv
. .venv/bin/activate
python -m pip install -r engine/requirements.txt
npx wrangler d1 migrations apply kiln --local
npm run validate
npm run devFor protected local operations without Access, create the git-ignored
.dev.vars file with the transition key:
KILN_API_KEY=replace-with-a-long-random-development-keynpm run validate type-checks the Worker and test suite, runs 17 Worker/MCP
integration tests and 30 engine tests, and performs a Wrangler dry run. GitHub
Actions also checks browser script syntax, moderate-or-higher production
dependency advisories, Python compilation, the engine Docker image, and a clean
local migration apply.
The checked-in Wrangler configuration names the production D1 database and R2
bucket. A new deployment must create its own resources and replace the D1 ID in
wrangler.jsonc before deploying.
npx wrangler r2 bucket create kiln-artifacts
npx wrangler d1 create kiln
npx wrangler d1 migrations apply kiln --remote
npm run deployBefore enabling browser or MCP writers, create one whole-host Cloudflare Access
application with no Path, enable Managed OAuth on that application, configure
CF_ACCESS_TEAM_DOMAIN and its single CF_ACCESS_AUD, and verify
/api/session through both flows. The MCP client URL still ends in /mcp; only
the Access application domain must omit the path. Set KILN_API_KEY only while
migrating existing automation or when a non-Access local environment needs it.
Always run the explicit migration command. As a deployment safety gate, the
Worker and Workflow also check migration 0003_build_provenance.sql before
database-backed work and apply that additive migration once if it is missing.
The runtime gate does not replace initial schema setup or an operator-reviewed
migration rollout.
ALLOWED_ORIGINS is an optional comma-separated list of exact http or
https origins allowed to make browser-origin MCP requests. Same-origin MCP is
always accepted. It does not configure REST CORS.
See docs/OPERATIONS.md for rollout, health, migration, reconciliation, and incident procedures.
The write smoke test is permanent public data because kiln has no project
delete operation. Use a unique, non-sensitive slug. The example needs jq and
assumes Bash:
export KILN_ORIGIN=https://kiln.timcf.workers.dev
export KILN_API_KEY='replace-with-the-deployment-key'
SLUG="smoke-$(date +%s)-${RANDOM}"
AUTH=(-H "Authorization: Bearer ${KILN_API_KEY}")
curl --fail-with-body -sS "${KILN_ORIGIN}/api/health"
curl --fail-with-body -sS "${KILN_ORIGIN}/api/engine/healthz"
curl --fail-with-body -sS \
-H 'Accept: text/markdown' "${KILN_ORIGIN}/"
curl --fail-with-body -sS -X POST \
"${AUTH[@]}" -H 'Content-Type: application/json' \
--data "{\"slug\":\"${SLUG}\",\"name\":\"Deployment smoke test\"}" \
"${KILN_ORIGIN}/api/projects"
SOURCE='import cadquery as cq
import os
part = cq.Workplane("XY").box(20, 16, 4, centered=(False, False, False))
os.makedirs("stl", exist_ok=True)
cq.exporters.export(part, "stl/smoke-box.stl")
'
curl --fail-with-body -sS -X PUT \
"${AUTH[@]}" -H 'Content-Type: application/json' \
--data "$(jq -n --arg path build.py --arg content "${SOURCE}" \
'{path:$path,content:$content}')" \
"${KILN_ORIGIN}/api/projects/${SLUG}/source"
QUEUED=$(curl --fail-with-body -sS -X POST \
"${AUTH[@]}" -H 'Content-Type: application/json' \
-H "Idempotency-Key: smoke-build-${SLUG}" \
--data '{"entry":"build.py","timeout_s":600,"printer_profile":{"x":180,"y":180,"z":180}}' \
"${KILN_ORIGIN}/api/projects/${SLUG}/builds")
BUILD_ID=$(jq -r .build_id <<<"${QUEUED}")
while :; do
BUILD=$(curl --fail-with-body -sS \
"${KILN_ORIGIN}/api/projects/${SLUG}/builds/${BUILD_ID}")
STATUS=$(jq -r .status <<<"${BUILD}")
printf '%s %s\n' "${BUILD_ID}" "${STATUS}"
case "${STATUS}" in verified|failed|cancelled) break;; esac
sleep 15
done
curl --fail-with-body -sS \
"${KILN_ORIGIN}/api/projects/${SLUG}/builds/${BUILD_ID}/artifacts"Also verify protected routes reject requests without an Access assertion or transition key:
curl -i -X POST -H 'Content-Type: application/json' -d '{}' \
"${KILN_ORIGIN}/api/projects/${SLUG}/builds"The expected status is 401.