Crafty Hosting CLI (crafty deploy)
@craftware/crafty-cli deploys a frontend's build output to Crafty Hosting.
The flow is: the CLI zips your build, uploads it to your Crafty tenant, and Crafty
ships it to the hosting provider on its own centralized account. You never touch the
provider directly — you only ever talk to "Crafty Hosting".
When to Use
- Starting a new site from an official starter:
npm create crafty/crafty create. - Deploying/shipping/publishing a built frontend (
./dist,.output/public,out/, …) to Crafty Hosting. - Running any
craftycommand:login,init,deploy,status,logout. - Creating or editing
crafty.json. - Wiring a custom domain to a deployed site, or debugging a failed deploy.
Scope
- In scope: using the published
@craftware/crafty-cliagainst a deployed Crafty tenant. - Out of scope: authoring content (use the
crafty-cmsskill / MCP server), building the Crafty backend, or the hosting provider's own dashboard. - Related: the same CLI generates TypeScript types for the site's content models —
crafty typeswritescrafty.types.tsfrom the environment API key (CRAFTY_API_KEY/--api-key, no login needed). Run it before writing code that reads entries or blocks; see the CLI README ("Content types").
New project: npm create crafty
For a new site, skip login + init below: npm create crafty@latest <dir> -- --url=https://<tenant>.crafty.do --yes (same as crafty create) copies an official starter (Astro "sitio de servicios"), logs in via the device flow with the scopes it needs (deploy crafty:read crafty:write crafty:publish), creates the site with the starter's content model and demo content (published unless --draft), and writes .env, crafty.json and src/crafty.types.ts. Then npm install && npm run dev, and npm run deploy (build + crafty deploy). Read the generated project's AGENTS.md before changing it. Flags and details: references/cli-commands.md.
Prerequisites
- Node.js 20+ and the CLI installed:
npm i -g @craftware/crafty-cli. - A built frontend — run the framework's build first (
npm run build) so the output directory exists. CRAFTY_API_URLpresent in the project's.env(the same URL the app uses to read the Crafty API). The CLI derives the tenant from it, so there is nothing else to configure.
The flow
crafty login # OAuth device flow — approve in the browser
crafty init # writes crafty.json (site, environment, buildDir)
npm run build # produce the build output (framework-specific)
crafty deploy # zips the build dir, uploads, prints the live URL
crafty login— prints a URL + short code; the user approves in a browser already signed into Crafty. The token (scopedeploy) is saved per tenant host to~/.crafty/credentials. No tokens are pasted by hand. The CLI keeps one session per tenant, so a user with several tenants stays logged into all of them and never re-logs in when switching projects. Usecrafty login --host=<url>to authenticate a tenant without being inside its project.crafty init— pick thesiteslug andenvironment(defaultproduction);buildDirdefaults todist. Non-interactive:crafty init --site=<slug> --environment=<env> --build-dir=<dir>.crafty deploy [dir]— packages the build dir (default fromcrafty.json), uploads it, polls untildeployed/failed, and prints the URL. Override the dir with a positional arg (e.g.crafty deploy .output/public).crafty status— shows the project's last deployment status/URL.crafty skills install— copies the Crafty agent skills into.claude/skills/(--globalfor~/.claude/skills/,--dir=<path>for other agents); re-run to update them.crafty logout— revokes and removes the session for the current project's tenant (other tenants stay signed in).crafty update(--update/-u) — self-updates the globally installed CLI to the latest version on npm (no-op if already current).
crafty.json
{
"site": "acme",
"environment": "production",
"buildDir": "dist"
}
Set buildDir to your framework's output: Vite/CRA dist, Next static export out, Nuxt .output/public, Astro dist.
How context is resolved (in order)
CRAFTY_API_URLfrom the project.env(a trailing/api/v1is stripped) → tenant base URL.crafty.json→site,environment,buildDir.CRAFTY_TOKENfrom the process environment (CI) or~/.crafty/credentials→ access token.
In CI (GitHub Actions)
There is no crafty login in CI: set CRAFTY_TOKEN (a personal access token with the deploy scope, from Perfil → Tokens in the panel) and CRAFTY_API_URL as environment variables, then run npx --yes @craftware/crafty-cli@latest deploy after the build. Never put CRAFTY_TOKEN in .env (the CLI ignores it there). To redeploy when content changes, point a Crafty content webhook with format "GitHub Actions" at https://api.github.com/repos/{owner}/{repo}/dispatches with an Authorization: Bearer <GitHub token> header, and trigger the workflow with on: repository_dispatch: types: [crafty-content] plus concurrency: { group: crafty-deploy, cancel-in-progress: true }. The CLI README has the full workflow.
Custom domains
A site gets an automatic, tenant-scoped subdomain ({tenant}-{hash}.crafty.run, one per environment, shown in Site → Hosting). To use your own domain, add it in the Crafty panel under Site → Hosting; Crafty returns a CNAME record to set at your DNS provider. The CLI deploys; domains are managed in the panel.
- Add a subdomain such as
www.example.com. Create only the CNAME Crafty shows and leave every other record alone, especially the email ones (MX, and the SPF, DKIM and DMARC TXT records). - A root domain (
example.com) only connects when its DNS is on Cloudflare. Never put a CNAME on the root: a CNAME cannot coexist with MX or TXT records, so it breaks the domain's email. Redirect the root towwwat the DNS provider instead. - Crafty never edits the customer's DNS; it only manages records for its own
crafty.runsubdomains.
Common pitfalls
No has iniciado sesión→ runcrafty loginfirst (in CI, setCRAFTY_TOKEN).No hay crafty.json→ runcrafty initin the project root.El directorio de build no existe→ run the framework build first, or fixbuildDir/ the positional dir argument.- No
CRAFTY_API_URL→crafty login/deploycan't find the tenant; add it to.env(or export it). - Deploying source instead of the build → point
buildDirat the compiled output, not the project root orsrc/. - Each deploy replaces the previous one for that environment — there is no per-branch preview or rollback in the CLI.
- Unknown URLs of a build without
404.htmlget the site'sindex.htmlwith a 200 (single-page app fallback), so client-side routes work. Missing files inside folders that hold no HTML (assets/,_astro/,fonts/…) return a real 404: Crafty Hosting adds a404.htmlthere at deploy time. Ship your own root404.htmlto turn the fallback off. Each deploy also keeps serving the previous deploy's hashed bundles (assets/index-<hash>.js), so a page cached before the deploy still loads.
References
references/cli-commands.md— every command, flag, exit behavior, and end-to-end examples per framework.references/install.md— installing the CLI, and adding the Crafty agent skills (crafty skills install, or zips for Claude.ai and Claude Desktop).