Documentation
Getting started: what to install and what to set up
Before your first Heddle command you need a few tools on your machine and a few accounts, and this page gives each one's install command for macOS, Windows and Linux. The minimum is Node.js 22 or later, git, the GitHub CLI and a Cloudflare account with your domain in it. Research and the agents add a Search Console property and a prepaid DataForSEO account, and email goes through Plunk. The rest is optional or off by default.
Install the tools first, then create the accounts. Each entry says what Heddle uses it for and which commands read it, so you can stop at what your install needs.
Tools
Node.js 22 or later, with npm
Heddle is a set of Node.js scripts. Every command runs on Node.js 22 or later, and npm installs Heddle's own dependencies.
| System | Install |
|---|---|
| macOS | brew install node |
| Windows | winget install OpenJS.NodeJS.LTS |
| Linux | curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash (installs nvm), then nvm install 22 (in a new terminal) |
Check it with node --version.
git
Heddle lives in a git repository beside your site's, and the agents work in branches and pull requests.
| System | Install |
|---|---|
| macOS | xcode-select --install |
| Windows | winget install Git.Git |
| Linux | sudo apt install git (Debian, Ubuntu), or sudo dnf install git (Fedora) |
Check it with git --version.
GitHub CLI (gh)
The agents open, read and merge pull requests with gh, heddle launch reads the repository's workflows and secret names with it, and you set the repository's secrets with gh secret set.
| System | Install |
|---|---|
| macOS | brew install gh |
| Windows | winget install GitHub.cli |
| Linux | sudo apt install gh (Debian, Ubuntu; or the packages at cli.github.com), or sudo dnf install gh (Fedora) |
Then:
gh auth login
Choose GitHub.com, HTTPS, and sign in with the browser. The account needs write access to the site's repository.
Check it with gh auth status.
Cloudflare's cf CLI (npm package cf, pinned at 1.0.0-beta.14)
Every Cloudflare step Heddle takes for you (DNS, Email Routing, KV, Worker secrets, Turnstile, tokens) goes through Cloudflare's own CLI, signed in once by a person. It is installed with Heddle. npm ci in the Heddle folder puts it in node_modules, so there is nothing to install separately and every machine runs the same version.
Install it with npm ci (in the Heddle folder).
Then:
npx cf auth login
Run it in the Heddle folder. A browser opens; sign in to the Cloudflare account the site deploys to.
Heddle always passes the account the site's wrangler.jsonc names, so one sign-in that sees several accounts never picks the wrong one. When different accounts need different sign-ins, cf keeps named profiles and binds one to a folder. npx cf auth create <name>, then npx cf auth activate <name> <path to the site>.
Run heddle from that folder so the binding applies. npx cf auth list shows the profiles and npx cf auth whoami the one in use.
Beta. cf is in beta. Heddle pins one version (1.0.0-beta.14) and maps each Cloudflare API call it makes to a cf command, so an update cannot change what a step does until Heddle moves the pin.
When cf has no command for a call, Heddle falls back to the Cloudflare API with a token if one is set, and otherwise reports the step as not readable rather than done.
Check it with npx cf auth whoami.
wrangler (Cloudflare's Workers CLI)
The site deploys to a Cloudflare Worker with wrangler, Worker secrets are set with wrangler secret put, and heddle media uploads to R2 with it. It is a dependency of the site, run with npx, so there is nothing to install globally.
Install it with npm install --save-dev wrangler (in the site's repository, once).
Then:
npx wrangler login
Check it with npx wrangler whoami.
Chromium or Google Chrome
heddle diagrams draws diagrams in a real browser with the brand's font, and the diagrams check measures their labels on the built page. Heddle looks for CHROMIUM_PATH, then Playwright's own download, then the usual install locations.
| System | Install |
|---|---|
| macOS | brew install --cask google-chrome |
| Windows | winget install Google.Chrome |
| Linux | sudo apt install chromium (Debian), or npx playwright install chromium (Ubuntu, in the Heddle folder), or sudo dnf install chromium (Fedora) |
Set CHROMIUM_PATH to the browser's executable if heddle diagrams says no Chromium could be launched.
ImageMagick, librsvg and zip
heddle brand draws the downloadable brand kit. rsvg-convert renders the SVG marks, magick sizes the PNGs and zip packs the kit.
| System | Install |
|---|---|
| macOS | brew install imagemagick librsvg zip |
| Windows | winget install ImageMagick.ImageMagick (and WSL for rsvg-convert and zip) |
| Linux | sudo apt install imagemagick librsvg2-bin zip |
Check it with magick -version && rsvg-convert --version.
ffmpeg
The video agent and heddle video render cut the narration and frames into a film with ffmpeg. Only needed when video is switched on.
| System | Install |
|---|---|
| macOS | brew install ffmpeg |
| Windows | winget install Gyan.FFmpeg |
| Linux | sudo apt install ffmpeg |
Check it with ffmpeg -version.
Get Heddle
Heddle is not published on npm. It is a git repository you are given read access to. Clone it beside your site's repository, so the site's scripts find it at ../heddle; set HEDDLE_DIR when it lives anywhere else.
git clone <the Heddle repository URL> heddle
cd heddle
npm ci
npm link
npm link puts the heddle command on your path. Without it, run node <path to heddle>/bin/heddle.mjs wherever the documentation says heddle. Check it with heddle on its own, which lists the 32 commands.
Accounts and keys
Cloudflare account and zone
The site runs on a Cloudflare Worker in your account, its domain is a zone in the same account, and the judging model (Workers AI) and the agents' Claude access (AI Gateway) are billed there.
Needed always; read by heddle agent, heddle install, heddle check, heddle deploy and heddle covers.
- Create a Cloudflare account, or use the one your sites already deploy to.
- Add the domain (Add a site) in that account and set the registrar's nameservers to the two Cloudflare gives you. Custom domains attach only from the zone's own account, so keep the zone and the Worker together.
- Copy the account id from the dashboard's Workers and Pages overview. It is an identifier that is safe to commit, so put it in wrangler.jsonc
account_idas well.
| Variable | What it holds | Where it is set |
|---|---|---|
CLOUDFLARE_ACCOUNT_ID |
the account id | your shell, GitHub secret |
Workers AI token (the judging model)
The model gates (helpful, buyer, quality, answers) and every agent's self-checks read pages with a model on Workers AI. Without the token those checks report SKIPPED, which never counts as a pass, and in CI heddle check fails on them.
Needed for the model checks and the agents; read by heddle agent, heddle check, heddle deploy, heddle launch and heddle covers.
- Cloudflare dashboard, My Profile, API Tokens, Create Token, the Workers AI template (Workers AI Read), limited to your account.
- site.json
model.tokenEnvnames the variable it is read from (CLOUDFLARE_API_TOKEN unless you set another, such as HEDDLE_AI_TOKEN);model.accountEnvnames the account variable.
| Variable | What it holds | Where it is set |
|---|---|---|
CLOUDFLARE_API_TOKEN |
the default name for the Workers AI token | your shell, GitHub secret |
HEDDLE_AI_TOKEN |
a common name for a dedicated Workers AI token, when site.json model.tokenEnv says so | your shell, GitHub secret |
Cloudflare AI Gateway (Claude for the agents)
Agents that draft (the writer, the SEO agent's titles and answers, covers, ideas) call Claude through an AI Gateway on unified billing: Cloudflare pays from the gateway's credits, so there is no separate model-provider key.
Needed for the agents that draft; read by heddle agent, heddle ideas and heddle covers.
- Cloudflare dashboard, AI, AI Gateway, Create gateway. Note its name.
- Add credits to the gateway and leave automatic top-up off; autonomy.json
budget.floorUsdstops the agents before it runs dry. - Create a token with AI Gateway Run (and Workers AI Read, so one token serves both).
- A 402 "insufficient credits" answer means the credits are empty; the token is fine.
| Variable | What it holds | Where it is set |
|---|---|---|
CF_AIG_TOKEN |
the gateway token | your shell, GitHub secret |
CF_AIG_GATEWAY |
the gateway's name | your shell, GitHub secret |
Cloudflare launch token
heddle launch --apply creates what a launch is missing (KV namespaces, Email Routing, the DMARC record, Turnstile hostnames) with a token made for that and nothing else. It never borrows another token to make changes.
Needed for heddle launch --apply; read by heddle launch.
- My Profile, API Tokens, Create Token, Custom token. Add Zone: Zone Read, DNS Edit, Email Routing Rules Edit, Zone Settings Edit, and Account: Email Routing Addresses Edit, Turnstile Edit, Workers KV Storage Edit, Workers Scripts Read.
- Account Resources. The account the sites deploy to. Zone Resources. All zones from that account.
- Keep it on your own machine. Launches run from a person's machine and never from CI, so it belongs in no repository and no GitHub secret.
| Variable | What it holds | Where it is set |
|---|---|---|
CLOUDFLARE_LAUNCH_TOKEN |
the launch token | your shell |
Cloudflare Turnstile
Turnstile is the contact form's spam check, and its widget must list the domain, or every enquiry from it is refused from the minute the domain goes live.
Needed for the contact form.
- Cloudflare dashboard, Turnstile, Add widget, with the domain and www as hostnames.
- The site key goes in the site's .env.local; the secret key is a Worker secret (
npx wrangler secret put TURNSTILE_SECRET_KEY).
| Variable | What it holds | Where it is set |
|---|---|---|
NEXT_PUBLIC_TURNSTILE_SITE_KEY |
the widget's site key (public) | your shell |
TURNSTILE_SECRET_KEY |
the widget's secret key | Worker secret |
Google Search Console property and service account
Positions, clicks, indexing and the weekly reports come from Search Console, read with a service account that has restricted (read-only) access to the property.
Needed for research, indexing and the SEO agent; read by heddle agent, heddle indexing, heddle research, heddle launch, heddle legacy and heddle data.
- Search Console. Add a Domain property for the domain and verify it with the TXT record, added in Cloudflare DNS.
- Google Cloud console. Create a project, enable the Google Search Console API, then IAM and admin, Service accounts, Create service account. Open it, Keys, Add key, JSON, and keep the downloaded file outside any repository (chmod 600).
- Search Console, Settings, Users and permissions, Add user, with the service account's email address and the Restricted permission.
- Check it.
heddle research <site> gsc checklists the properties the key can read.
| Variable | What it holds | Where it is set |
|---|---|---|
GSC_KEY_FILE |
the path to the key file, on your machine | your shell |
GSC_SERVICE_ACCOUNT_JSON |
the whole key file's contents, for the agents | GitHub secret |
DataForSEO
Search volume, cost per click, live results pages and position tracking. Prepaid. It fails by running out, and an empty balance answers exactly like a wrong password.
Needed for research and the agents that measure demand; read by heddle agent, heddle research, heddle propose and heddle data.
- Sign up at dataforseo.com and add funds. The API login and password are on the API Access page; the password is not your account password.
- Before blaming the key, check the balance with
heddle research <site> balance. autonomy.jsonbudget.dataFloorUsdstops the agents' data calls under that balance.
| Variable | What it holds | Where it is set |
|---|---|---|
DATAFORSEO_LOGIN |
the API login | your shell, GitHub secret |
DATAFORSEO_PASSWORD |
the API password | your shell, GitHub secret |
OpenSEO (optional)
An alternative source for search data, declared in site.json data.providers. Heddle reads it and never starts a paid run or creates a project on it.
Needed optional.
- Take the API URL and key from your OpenSEO account, and declare it in site.json
data.providers.
| Variable | What it holds | Where it is set |
|---|---|---|
OPENSEO_API_KEY |
the OpenSEO key | your shell, GitHub secret |
Bing Webmaster Tools
Bing's index feeds Bing, Copilot and ChatGPT search, and Google's tools see none of it. IndexNow submissions report into the same account.
Needed recommended at launch.
- bing.com/webmasters, sign in, Import from Google Search Console. It verifies the domain and brings the sitemap with it.
- Check that the sitemap is listed, then read Search Performance and AI Performance there each week.
| Variable | What it holds | Where it is set |
|---|---|---|
BING_WEBMASTER_API_KEY |
an API key from Bing Webmaster Tools, Settings, API access, when site.json data.providers declares bing | your shell, GitHub secret |
PageSpeed Insights API key (optional)
The design and development agent reads Core Web Vitals from PageSpeed Insights. It works without a key at a low volume; a key raises the quota.
Needed optional; read by heddle data.
- In the Google Cloud console project that holds the service account, enable the PageSpeed Insights API, then Credentials, Create credentials, API key. Restrict it to that API.
| Variable | What it holds | Where it is set |
|---|---|---|
PAGESPEED_API_KEY |
the API key | GitHub secret |
Plunk (email out)
The contact form, the agents' reports and the signed approval links are sent through Plunk, from your own domain.
Needed for email from the site and the agents; read by heddle agent, heddle indexing, heddle ideas and heddle launch.
- Create a project at useplunk.com and copy its secret API key (sk_).
- Project settings, Domains, Add the domain. Add the DKIM, SPF and bounce records it shows in Cloudflare DNS as DNS only, then press Verify. Leave out the inbound MX at the bare domain, which stops Cloudflare Email Routing from enabling.
- The key is a Worker secret for the contact form and a GitHub secret for the agents' reports.
| Variable | What it holds | Where it is set |
|---|---|---|
PLUNK_API_KEY |
the secret API key | your shell, GitHub secret, Worker secret |
PLUNK_PROJECT_ID |
optional: lets heddle launch read the domain's status from Plunk's API | your shell |
GitHub repository, secrets and tokens
The agents run as GitHub Actions in the site's repository. They need read access to Heddle and, for ideas by email, a token the Worker uses to start a run.
Needed for the agents.
- Copy templates/github/heddle-agents.yml into the site's .github/workflows/.
- Create a fine-grained personal access token with Contents read-only on the Heddle repository, and set it as the site's HEDDLE_REPO_TOKEN secret with
gh secret set HEDDLE_REPO_TOKEN -R <owner>/<site>. A read-only deploy key (HEDDLE_DEPLOY_KEY) works instead where the organisation allows deploy keys. - Set every other secret the same way, without echoing a value.
| Variable | What it holds | Where it is set |
|---|---|---|
HEDDLE_REPO_TOKEN |
read access to the Heddle repository | GitHub secret |
HEDDLE_DEPLOY_KEY |
or: a read-only deploy key on the Heddle repository | GitHub secret |
GITHUB_DISPATCH_TOKEN |
ideas by email: a fine-grained token on the site's repository with Actions read and write, so the Worker can start the ideas workflow | Worker secret |
Secrets you generate
Shared secrets between the agents and the site's Worker. Nobody issues these; make each one a long random string, for example with openssl rand -hex 32, and set it in both places.
Needed for approvals, leads and ideas; read by heddle ideas.
- Generate the value, then
npx wrangler secret put <NAME>in the site andgh secret set <NAME> -R <owner>/<site>.
| Variable | What it holds | Where it is set |
|---|---|---|
HEDDLE_APPROVAL_SECRET |
signs the approval links; the Worker verifies them | GitHub secret, Worker secret |
HEDDLE_LEADS_TOKEN |
lets the follow-up agent read new enquiries from the Worker | your shell, GitHub secret, Worker secret |
HEDDLE_IDEAS_TOKEN |
ideas by email: signs the approve and withdraw links and reads the ideas the Worker holds | GitHub secret, Worker secret |
Reddit API app (optional)
heddle research <site> reddit quotes buyers' own words from Reddit threads. Hacker News needs nothing.
Needed optional; read by heddle research.
- reddit.com/prefs/apps, create an app of type script, and use its client id and secret with your username.
| Variable | What it holds | Where it is set |
|---|---|---|
REDDIT_CLIENT_ID |
the app's client id | your shell |
REDDIT_CLIENT_SECRET |
the app's secret | your shell |
REDDIT_USERNAME |
your Reddit username, for the user agent | your shell |
ElevenLabs (video narration, off by default)
The video agent narrates explainers with an ElevenLabs voice. Video is off until this is set.
Needed for video; read by heddle video.
- Copy an API key from your ElevenLabs profile. To use a cloned voice, put its id in the variable autonomy.json
runtime.video.voiceIdEnvnames.
| Variable | What it holds | Where it is set |
|---|---|---|
ELEVENLABS_API_KEY |
the API key | GitHub secret |
ELEVENLABS_VOICE_ID |
optional: the voice id | GitHub secret |
YouTube channel (video publishing, off by default)
heddle video publish uploads a merged, rendered film to your channel. A person runs it; no agent uploads.
Needed for heddle video publish; read by heddle video.
- Google Cloud console. Enable the YouTube Data API v3 and create an OAuth client with the youtube.upload and youtube.force-ssl scopes. The channel's owner authorises it once and you keep the refresh token. Until Google's API audit passes, uploads stay private.
| Variable | What it holds | Where it is set |
|---|---|---|
YOUTUBE_CLIENT_ID |
the OAuth client id | your shell |
YOUTUBE_CLIENT_SECRET |
the OAuth client secret | your shell |
YOUTUBE_REFRESH_TOKEN |
the channel's refresh token | your shell |
Where the values live
Values live in three places, and never in a repository.
Your shell, for commands you run. Keep the values in a file outside every repository with permissions 600, and load it only in the shell that needs it with
set -a; . <file>; set +a. Check that the file ends with a newline before you append to it.GitHub Actions secrets, for the agents, set with
gh secret set <NAME> -R <owner>/<site>, which reads the value without echoing it.Worker secrets, for the site itself, set with
npx wrangler secret put <NAME>in the site's repository.
heddle research <site> check says which data sources are configured, and heddle launch <site> lists every secret the Worker and the repository still lack.
Generated from the Heddle repository at ddb1b16.
Questions
Do I need every account on this page?
No. Node.js, git, the GitHub CLI and a Cloudflare account are needed for any install. Search Console and DataForSEO are needed for research and for the agents that measure. The rest are optional, or for video, which is off by default.
Where should the keys live?
Keys you use from your own machine live in your shell's environment, loaded from a file outside every repository. The agents read theirs from GitHub Actions secrets, and the site's Worker from its own secrets. None of them is ever committed.