CLAUDE CODE MARKETPLACES

weft-deploying

Read when the user wants weft or a program live for real people on their cloud, asks about prod, a target, `--on`, a domain or GCP, or a deploy failed: targets and logging in, domains, the deploy workflow `weft ci add` writes, handing it its settings with `weft target export`, a project's frontends (`weft frontend`), what a cloud build is and how it fails, rolling back, and connections on a cloud install. The deployer specialist reads it; you read it before dispatching one.

npx skills add https://github.com/WeaveMindAI/weft --skill weft-deploying
SKILL.md

Deploying

A [target] is one weft install, named in the project's weft.toml:

[targets.prod]
url = "https://weft-role-dispatcher-123456789.us-central1.run.app"

local is always a target: the install on this machine, at http://127.0.0.1:14111 unless it was started on another port (the public field of ~/.local/share/weft/ports.json says). Every weft command acts on local unless it is given --on <target> (or WEFT_DISPATCHER_URL is set in its environment, which you never do), and no setting makes another target the default.

Only the deployer specialist passes --on. You never do: when the user asks for anything on a cloud install, you dispatch the deployer with what they asked, and relay its report.

If weft is not on the user's cloud yet, or they want to upgrade or resize it, Tangle reads the weft-cloud-install skill and does it in conversation: the user has to log in to gh and gcloud along the way, so it is never handed to a specialist, which cannot talk to the user.

A name instead of the install's own address

The install answers at its own Cloud Run address (https://...run.app) from the start, over HTTPS, for free. That address is enough for the CLI, frontends and providers' webhooks; a domain gives the install a name people see.

A domain costs money: it needs a Google load balancer in front of the install, about $18 a month plus $0.008 per GB through it, for as long as the install has any domain. weft makes it with the first domain and takes it down with the last. So before the first domain, tell the user that price and go on only once they agree. With their yes, the deployer runs weft domain add <name> --on prod --accept-cost (without --accept-cost the first domain is refused, naming the price). It prints the DNS record to set (type A, the name, the load balancer's address), which the deployer hands back for the user to set at their registrar; Google then issues the certificate on its own once it sees the record. weft domain list --on prod shows every domain and its record.

A domain can serve one project instead of the whole install: --for api serves that project's routes at the root of the domain (https://api.shop.com/users/42, on top of <install address>/connect/local/users/42), and --for frontend --to <its https address> passes the domain on to the project's frontend (weft frontend ls --on prod shows that address).

Workers and long runs

weft workers shows a project's worker settings (copies kept warm, the most copies, runs per copy, CPU, memory) and which come from the install. Before you run weft workers set --min-instances 1, tell the user: it keeps one copy warm so the first call never waits for a start, and on a cloud install that copy is billed all the time. A run that may take longer than an hour on a cloud install (Cloud Run cuts a request there) runs with weft run --long, or with longRuns on the trigger that starts it, and gets a worker of its own for up to seven days.

A trigger that holds a connection open (a stream or a socket it listens to, an event subscription that dials out) runs on a holder, which weft starts only while such a trigger is on and which is billed while it runs. Its node shows "waiting for a holder to take it" until one has. While a holder runs, it checks in through weft every 10 seconds, which also keeps weft's broker and the database up. Infrastructure that is up also keeps the broker and the database awake: the supervisor checks its health every 30 seconds, through the broker. Everything else (a route, a form, a timer, a poll, a provider that pushes its events) needs nothing running between events. If the install's database is on a plan that sleeps and has limited hours, tell the user before you deploy such a trigger, or infrastructure, that it keeps the database awake around the clock.

Setting a project up for its cloud

Somebody installed weft on their cloud once (above), and its summary printed the install's address. What a project needs from there, in order, each run from the project's folder:

  1. weft target add prod <address>. It writes [targets.prod] into weft.toml, which is committed, so the team shares the name.
  2. weft login prod: asks for an operator key in a hidden prompt, and checks it against the install before storing it in ~/.config/weft/credentials.toml, which only the user can read. The user types the key into their own terminal; you never ask them for it, and never pipe one in (--key-stdin is for the user's own scripts).
  3. weft ci add --cloud gcp: writes .github/workflows/deploy.yml. The workflow, run by hand from the repository's Actions tab, builds the CLI of the exact weft the install runs, deploys the program (weft activate --on <target> when it is off, weft resync --on <target> --mode park when it is on), then, when the install hosts a frontend for the repository, builds front/ with Docker and runs it on that frontend's Cloud Run service, which calls the install at its address and reaches the program's infrastructure over the install's private network. weft new --ci gcp writes the same file when the project is made. If it refuses because the file "was changed since weft wrote it (or weft never wrote it)", the file is somebody's own and weft never overwrites it: tell the user, and leave it.
  4. weft target export prod --github: needs step 2 first, and the repository already on GitHub. It sets the variables and secrets the workflow reads, with gh (logged in, run inside the repo): it mints an operator key for CI, and gives the frontend the install hosts for this repository a new token (the old one works until the workflow's next run deploys the new one and retires it) and names its service. Every run mints new ones, so it runs once per setup, or again when a CI credential was lost. Without --github it prints them, and the secrets are shown only that once.

If the project has a frontend (front/Dockerfile), register it on the install between steps 3 and 4 (next section). A project with no frontend skips that: the workflow deploys the program and stops.

A project's frontend: weft frontend

A frontend is a website that calls the install on its visitors' behalf. The install knows each one by a name you pick, and gives it a token of its own that can only act on this project. Where the site runs decides how you add it.

If the install should host it (Cloud Run, deployed by the project's GitHub workflow), name the repository whose workflow deploys it:

weft frontend add front --repo <owner/name> --on prod
weft target export prod --github

The first command reads the repository's id with gh (logged in; access is granted to that id, so the name changing hands later gives nobody anything), makes the frontend's Cloud Run service (empty until the first deploy) and lets that repository's workflow deploy to that service and nothing else on the install. It prints the service's name and the address visitors will reach. The export then hands the repository the service's name and a fresh token for the frontend, and the next run of the deploy workflow builds front/ and puts it there. The order matters: an export run before the frontend exists hands over no frontend, and the workflow skips it.

Every hosted frontend deploys as the install's one deploy account, so a repository you add here could deploy to another project's frontend too. Only add repositories the user trusts with all of them, and ask before adding one the user did not name.

If it runs somewhere else (Vercel, the user's own server, this machine), leave out --repo:

weft frontend add shop --on prod

The install makes nothing. The command writes the frontend's token, with the install's address, as WEFT_TOKEN, WEFT_DISPATCHER_URL and WEFT_PUBLIC_URL, to a file under ~/.local/share/weft/exports/ that only the user can read. Tell the user that path: they copy the three into the site's environment. Never print the token or put it in the chat. A local install hosts nothing, so locally this is the only form, and --repo is refused.

Afterwards:

  • weft frontend ls --on prod lists the project's frontends: each one's name, where it runs, the repository that deploys it, and its address.
  • A new token never breaks the running site: the old one keeps working until the new one is in place. For a hosted frontend, weft target export prod --github makes the new one and hands it to the workflow, and the workflow's next run deploys it and then retires the old one itself. For one that runs elsewhere, weft frontend token <name> --on prod writes a new one to the private file and prints its id; once the user has put it in the site's environment, weft frontend token <name> --done <id> --on prod puts it in place and retires every other token of the frontend.
  • weft frontend rm <name> --on prod removes one: its tokens stop working, and a hosted one's service is deleted with whatever runs on it. Ask the user before running it. If the service cannot be removed, it stops and says why; --force forgets the frontend anyway and names what stays on the cloud. weft rm on the project removes its frontends first, and weft rm --force does the same with --force.

A name is 1 to 20 lower-case letters, digits or inner hyphens, starting with a letter, and is taken once per project.

What a deploy does

weft activate --on prod compiles the project here first (a compile error fails fast, on this machine), then uploads a snapshot of the project's sources, only the files the install does not have yet. The install compiles that snapshot itself, builds the images it needs, and activates. So a deploy of an unchanged project builds nothing.

Activate never starts infrastructure. On a first deploy of a program with infra (a database, a bridge), run weft infra start --on prod first: if a trigger reads infra that is not running, activate refuses with "these triggers' infra is not running: <node>". On GCP each piece boots a machine, so it takes a few minutes; weft status --on prod shows each one's state.

weft activate is for a program that is off. Once its triggers are on, it refuses, and a change goes live with weft resync --on prod --mode <mode>, which takes the triggers down and brings them back on the new version. On prod, pick park (calls that arrive meanwhile wait and run once the triggers are back) or hibernate (they keep being answered for a grace window), because those calls can be real people's. wipe drops them and cancels the work waiting on the triggers: fine on a dev install, and on prod only when the user says so.

Every target keeps its own versions: weft tree --on prod lists what was deployed there. To roll back, put an older one's files back in the folder with weft branch <id> --on prod, then weft resync --on prod --mode park (or weft activate --on prod if the program is off). Its images are already built, so nothing rebuilds. weft branch refuses while the folder holds changes no version records; --discard throws those changes away, and you pass it only when the user said to.

The frontend on a cloud install

The frontend's server reads three variables, set by the workflow on Cloud Run and by front/.env on this machine:

VariableOn a cloud installOn this machine
WEFT_DISPATCHER_URLthe install's addresshttp://127.0.0.1:14111
WEFT_TOKENthe frontend's own token, which weft target export renews and hands the workflowa token from weft frontend add <name> (no --repo), written to a private file
WEFT_PUBLIC_URLthe install's public addresshttp://127.0.0.1:14111

On this machine, 14111 is the default port; if the install was started on another one, ~/.local/share/weft/ports.json has it (public).

What else the server reads (the program's database from weft infra env, BETTER_AUTH_SECRET) goes to Cloud Run as one dotenv file: the user writes it on their machine with weft infra env <node> --on prod --into front/.env.prod plus the door's address from weft infra list-doors --on prod, and weft target export prod --github --front-env front/.env.prod stores it as the WEFT_FRONT_ENV secret. The deployer only runs the export; the file holds secrets the user writes.

front/Dockerfile builds a server that listens on $PORT. If the user's frontend is not a SvelteKit app, only the workflow's "build the frontend" step changes.

Connections

Each install keeps its own connections and its own picks of them: which connection each access node uses is picked on the install, never written in the source. A node connected on this machine has no pick on prod until one is made there: weft connect --on prod --node <node> --list shows prod's stored connections, --grant <id> picks one (a live trigger reading it is set up again), and a connection prod does not hold yet is the user's to add, with the command you hand them (weft connect --on prod --node <node>). A run on prod that reaches a node with nothing picked there is refused before it starts, naming that command.

An instance's connection (a field written @instance_filled) lives in the install it was made on, so instances connect on prod through the site, as they do here.

When a deploy fails

  • A compile error: printed before anything leaves this machine. The same fix as on local.
  • An image build failed: the install's builder log is quoted in the error, the cargo or apt line included. Fix the node it names; the next deploy rebuilds only what changed.
  • 401 from the install: the key is wrong or was revoked. The user runs weft login prod again; in CI, weft target export prod --github again.
  • 429 on a route: one of the route's limits turned the call away. weft status --on prod lists, under "refused calls", which node refused, how often in the last two minutes, and by which limit; each limit is a setting on the route's node.
Installs0
GitHub Stars2.0k
LanguageRust
AddedOct 3, 2026
View on GitHub