# 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.

{% steps %}

{% step title="Build + compile test" %}
Generate the production build. It's your **compile test**: if Vite bundles the app without errors, the code is sound enough to publish.

```bash
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:

```bash
fayz doctor
```

```
⚠ plugins referenced by manifest are resolved by the platform bundle, not public npm packages: dashboard

0 error(s), 1 warning(s)
```

{% callout type="tip" %}
✓ 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.
{% /callout %}
{% /step %}

{% step title="Push to GitHub" %}
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:

```bash
# at the app root, make sure .fayz/ is ignored
echo ".fayz/" >> .gitignore
```

Now initialize the repository and make the first commit:

```bash
git init
git add .
git commit -m "feat: my Fayz app"
```

Create the remote repository — via the GitHub CLI or the website — and push:

```bash
# 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
```

{% callout type="tip" %}
✓ 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.
{% /callout %}
{% /step %}

{% step title="Authenticate with `fayz login`" %}
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.)

```bash
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:

```bash
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.

{% callout type="tip" %}
✓ 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.
{% /callout %}
{% /step %}

{% step title="Inspect with `fayz deploy --dry-run`" %}
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**:

```bash
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.

{% callout type="tip" %}
✓ 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.
{% /callout %}
{% /step %}

{% step title="Publish with `fayz deploy`" %}
Now upload for real:

```bash
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
```

{% callout type="info" %}
**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](/en/docs/resources/community). The [static host](/en/docs/deploy/static) path puts the same app live in the meantime.
{% /callout %}

{% callout type="tip" %}
✓ 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`.
{% /callout %}
{% /step %}

{% /steps %}

## 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.ai` loads 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](/en/docs/tutorial/05-real-data-with-supabase)).

**Alternative: static host.** Prefer Vercel, Netlify or Cloudflare Pages? The same `dist/` goes up on any file host — see [Static deploy](/en/docs/deploy/static).

## 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](/en/docs/plugins) and build your next product.
- Turn the app into a layer an agent reads and operates in [AI · Your app as a data layer](/en/docs/ai/your-app-as-a-data-layer).
- Go deeper on your own plugins in [Incubator](/en/docs/building-plugins/incubator).

---

[← Previous: 06 · Your own plugin](/en/docs/tutorial/06-your-own-plugin) · [Back to the tutorial start](/en/docs/tutorial)
