# Install vaskel. on your own Cloudflare account

Give your coding agent this request:

> Set up vaskel. using https://vaskel.com/INSTALL.md. Guide me through any prerequisites.

The agent checks what is ready and guides account sign-in and domain setup as
needed. One instance serves the browser app, every circle and all direct
messages, using the operator's own Cloudflare resources.

## Rules for the agent

Never print `AUTH_SECRET`, `REALTIME_APP_SECRET` or the contents of a secrets
file into chat or logs. The setup key is the one exception: deliver it to the
person separately from the bootstrap URL. Keep all secrets out of Git.

Use only the operator's account, hostnames and resources. The repository's
account IDs and hostnames belong to the maintainers. Confirm new purchases,
including a Workers paid plan or domain, before making them; usage beyond
included allowances is billed separately.

Resume an interrupted installation from its existing checkout, configuration,
resources and secrets. Do not recreate resources or regenerate secrets just
because a later step failed. Report the failed step and what is needed to
continue.

## Prepare together

1. Ask which Cloudflare account to use, the exact HTTPS origin for the app
   (for example `https://chat.example.org`), and the sender address for
   verification mail (for example `vaskel@example.org`). Help choose any missing
   values. The account needs the Workers paid plan, starting at US$5 a month,
   and an active domain on Cloudflare.

2. Check Git, Node 24.14 or newer and pnpm 11. Help install missing tools, then
   clone vaskel unless the installation already has a checkout:

   ```sh
   git clone https://github.com/zllovesuki/vaskel.git vaskel
   cd vaskel
   pnpm install --frozen-lockfile --ignore-scripts
   ```

   Create or use a repository owned by the operator, preserving vaskel's Git
   history. Set that repository as `origin` and retain the original as
   `upstream`. Guide repository sign-in if needed. Save the instance's
   nonsecret configuration and customizations there.

3. Use the installed Wrangler, checking the logged-in account first. Guide the
   browser login only if needed:

   ```sh
   pnpm exec wrangler whoami
   pnpm exec wrangler login
   ```

4. Check the chosen domain and guide
   [Cloudflare Email Service setup](https://developers.cloudflare.com/email-service/get-started/)
   for the sender. The app and sending addresses can use the same domain.
   Wait for domain verification before deployment.

5. Help create or select the operator's
   [Realtime SFU app](https://developers.cloudflare.com/realtime/sfu/get-started/).
   Have the person supply `REALTIME_APP_ID` and `REALTIME_APP_SECRET` through
   the agent's local environment, never chat. Confirm the secret is present
   without displaying it. Keep the same app when resuming setup.

## Configure and deploy

Run commands from the repository root. The commands below use the default
resource names; use the instance's recorded names when resuming.

1. Edit `env.production` in `apps/server/wrangler.jsonc`: set `account_id`, an
   available Worker `name`, the custom-domain route, `vars.AUTH_ORIGIN`,
   `vars.MAIL_FROM` and `vars.REALTIME_APP_ID`. Use unused account-scoped IDs
   for the four rate-limit bindings. Preserve every Durable Object class,
   binding and migration declaration.

2. Check for this installation's D1 database and private R2 bucket in the
   selected account. Reuse matching resources recorded by an earlier attempt;
   do not adopt an unrelated resource just because its name matches. Create
   only missing resources:

   ```sh
   pnpm exec wrangler d1 create vaskel-identity-production --config apps/server/wrangler.jsonc --env production
   pnpm exec wrangler r2 bucket create vaskel-attachments-production --config apps/server/wrangler.jsonc --env production
   ```

   Record the D1 database ID and both resource names in the production
   bindings. If a name is taken by another installation, choose a distinct
   name and update its binding.

3. For a new installation, generate secrets once in a protected, ignored file.
   If that file already exists, reuse it instead of running this command:

   ```sh
   node --input-type=module <<'NODE'
   import { randomBytes } from "node:crypto";
   import { mkdirSync, writeFileSync } from "node:fs";
   const secret = process.env.REALTIME_APP_SECRET;
   if (!secret?.trim()) throw new Error("REALTIME_APP_SECRET is missing");
   mkdirSync(".local/install", { recursive: true, mode: 0o700 });
   writeFileSync(".local/install/secrets.json", JSON.stringify({
     AUTH_SECRET: randomBytes(32).toString("hex"),
     OPERATOR_BOOTSTRAP_SECRET: randomBytes(32).toString("hex"),
     REALTIME_APP_SECRET: secret,
   }), { mode: 0o600, flag: "wx" });
   NODE
   ```

4. Apply the identity migrations by binding name:

   ```sh
   pnpm exec wrangler d1 migrations apply IDENTITY_DB --remote --env production --config apps/server/wrangler.jsonc
   ```

   Once the account, domain and resources are ready, set
   `vars.OPERATOR_BOOTSTRAP_EXPIRES_AT` to an ISO timestamp about one hour
   ahead, then build and deploy:

   ```sh
   CLOUDFLARE_ENV=production pnpm run build
   pnpm exec wrangler deploy --config apps/client/dist/vaskel/wrangler.json --secrets-file .local/install/secrets.json
   ```

   Remote secrets persist after deployment. If deployment already succeeded,
   subsequent attempts can omit `--secrets-file`; do not replace existing
   secrets. Commit and push the nonsecret configuration and source changes to
   the operator's repository.

## Become the host and invite someone

1. Verify HTTPS, `/api/health` and `/api/identity/bootstrap`; the bootstrap
   response must report `available`, not merely serve the HTML page. Inspect
   startup errors with
   `pnpm exec wrangler tail --config apps/server/wrangler.jsonc --env production`
   if needed. A bootstrap status of `complete` means the host already exists.

2. Give the person their origin followed by `/bootstrap`, then provide the
   setup key separately. They choose their email, display name and password,
   follow the verification email, and sign in. The setup key is single-use;
   the first public visitor never becomes the host. If the unused key expires,
   refresh the expiry and rebuild/redeploy while preserving resources and
   secrets. If verification mail is missing, fix delivery and use resend
   verification rather than trying to create the host again.

3. Guide the host to create a circle and its rooms from Home, then create an
   invitation in circle settings. Have a guest register, verify their email
   and try a message, a call and screen sharing from another device. Report
   any checks still waiting on the person separately from the deployment result.

4. After successful deployment and host sign-in, delete the temporary local
   secrets file. Hand over the app URL and the operator's repository URL.
   Further administration and diagnostics are in
   [Operations](https://github.com/zllovesuki/vaskel/blob/main/docs/operations.md).
