07 · Publish
Time to put the app live. The flow is straightforward: you generate the build (which doubles as your compile test), push the code to GitHub and run fayz deploy — one command that handles auth, connects the project to the platform and publishes it on its own URL. By the end of this step you have a https://<your-app>.live.fayz.ai link anyone can open.
The fayz commands below run via npx @fayz-ai/cli <command> (nothing installed globally); the examples use the short fayz form for readability.
Generate the production build. It's your compile test: if Vite bundles the app without errors, the code is sound enough to publish.
npm run build
Real output:
vite v5.4.21 building for production... ✓ 34 modules transformed. dist/index.html 0.40 kB │ gzip: 0.27 kB dist/assets/index-tn0RQdqM.css 0.00 kB │ gzip: 0.02 kB dist/assets/index-BERJrSU-.js 143.00 kB │ gzip: 46.00 kB ✓ built in 274ms
The result is a dist/ folder ready to serve. Next, run doctor to validate the manifest one last time:
fayz doctor
⚠ plugins referenced by manifest are resolved by the platform bundle, not public npm packages: dashboard 0 error(s), 1 warning(s)
✓ You should see: ✓ N modules transformed and ✓ built in … from the build, and 0 error(s) from doctor. The warning about plugins resolved by the platform bundle is expected — it isn't an error. If the build fails, this is where you find out, before uploading anything.
Your app is your repository. Before committing, add the .fayz/ folder to .gitignore — it holds the local deploy link (.fayz/project.json) and shouldn't go into the repository. The scaffold doesn't cover this yet, so do it manually:
# at the app root, make sure .fayz/ is ignored echo ".fayz/" >> .gitignore
Now initialize the repository and make the first commit:
git init git add . git commit -m "feat: my Fayz app"
Create the remote repository — via the GitHub CLI or the website — and push:
# with the gh CLI gh repo create my-store --private --source=. --push # or, if you created the repo on the website: git remote add origin https://github.com/<your-user>/my-store.git git push -u origin main
✓ You should see: .fayz/ listed in .gitignore before the first git add, and the repository on GitHub with your code — without the .fayz/ folder. Check on the GitHub site that it doesn't show up.
Deploying needs an access token. Generate one under Fayz → Settings → Access tokens — it comes with the fayz_ prefix. (Deploy access is rolling out to the invited developer network.)
fayz login
Paste the token when prompted. Real output:
✓ Token salvo em ~/.fayz/credentials.json (permissão 0600). fayz_xxx…xxx O token será validado no primeiro deploy (nenhuma chamada de rede foi feita agora).
Login is local: it only writes the token with 0600 permissions and validates it later, on the first deploy. Useful commands:
fayz login --status # shows the masked token, if there is one fayz logout # removes credentials.json
On CI or ephemeral machines, use the FAYZ_TOKEN environment variable instead of fayz login — the CLI reads it directly.
✓ You should see: ✓ Token salvo em ~/.fayz/credentials.json. fayz login --status should show the masked token. No network call happens at login — validation is deferred to the deploy.
Before uploading for real, run the dry run. It lists exactly what would be sent and where it would go, with no network calls at all:
fayz deploy --dry-run
Real output:
▸ Arquivos a enviar de my-store: app.manifest.json (1087 B) index.html (299 B) … Total: 13 arquivo(s), 7099 B. Destino: novo projeto 'my-store' (será criado no primeiro deploy) (dry-run — nenhuma chamada de rede foi feita)
The project name is derived from the name field in your package.json. Check the list and the destination before going further.
✓ You should see: the file list with sizes, the Total, the Destino: novo projeto '<name>' line and the (dry-run — nenhuma chamada de rede foi feita) line. If the destination or the file count surprises you, sort it out before the real deploy.
Now upload for real:
fayz deploy
The CLI confirms the destination before acting (use --yes to skip the confirmation; in a non-interactive shell it fails fast instead of acting on its own). On the first deploy it creates the project on the platform and writes .fayz/project.json with { projectId, name }; on subsequent deploys it reuses that file, shipping to the same project. The code uploads in batches, the platform builds on the server (install + Vite) and returns the final URL:
🔗 https://my-store.live.fayz.ai
Access is rolling out. fayz deploy is being opened up to the invited developer network. If the deploy returns an authorization error, your token doesn't have CLI access enabled yet — reach out to us in the community. The static host path puts the same app live in the meantime.
✓ You should see: the https://<your-app>.live.fayz.ai URL at the end of the deploy, and a new .fayz/project.json at the root (ignored by git). Open the URL — the app should load just like npm run preview.
Did you actually ship?
Publishing isn't just the command succeeding — it's the app working for whoever opens the link. Check:
- The URL opens —
https://<your-app>.live.fayz.ailoads without a blank screen. - The main flow works — that one essential path through your product goes end to end.
- Data persists — what you create in one session is still there in the next (Supabase connected in step 05).
Alternative: static host. Prefer Vercel, Netlify or Cloudflare Pages? The same dist/ goes up on any file host — see Static deploy.
What now?
Zero to live: you generated the app, understood the anatomy, applied the brand, turned on plugins, connected a real database, wrote your own plugin and published it — all from the manifest. Where to go next:
- Explore the plugin catalog and build your next product.
- Turn the app into a layer an agent reads and operates in AI · Your app as a data layer.
- Go deeper on your own plugins in Incubator.
← Previous: 06 · Your own plugin · Back to the tutorial start