Install
openclaw skills install @cargo-ai/website-buildingBuild the company website on Cargo and serve it on your own domain: a statically exported Next.js app, one defineApp with its www hostname, and a defineDomain that publishes the records and forwards the apex, changed only through reviewed pull requests. Triggers: "build our company website", "recreate our website on Cargo", "host our marketing site on Cargo", "put our website on our own domain", "capture our design system for the website", "keep our website updated through pull requests". Cargo CDK, defineApp, defineDomain, domainRecords, Next.js, Tailwind, shadcn/ui. Skip when: you are researching another company's website, which is research-account; or you want a generic hosted app, dashboard or webhook, which is cargo-hosting in the Cargo skill pack.
openclaw skills install @cargo-ai/website-buildingState: to-be-approved. Deploy-verified against a live workspace: not yet. Treat Done when
below as the acceptance test and review cargo-ai cdk plan before deploying. Make no outcome claim
for this skill until it is approved.
The company website lives in the company's repository, is hosted by Cargo, and answers on the company's own name. Every change, from a typo to a new page, is a pull request someone reviewed before it deploys. Your coding agent writes those pull requests; nothing is deployed to do it.
Three resources make the site:
defineApp uploads the Next.js package in infra/apps/website/, and Cargo runs
its build: every page is exported as HTML with its own URL, title and description. The starter
has home and about pages, robots.txt, a sitemap, a theme toggle and shadcn/ui on Tailwind,
with the copy in site.json.domains: ["www.example.com"]. Cargo's own *.app.getcargo.run
URL is noindex; the company's www host is what search engines index.defineDomain publishes the app's records and forwards
the apex to www. When the DNS lives at another provider, the file is deleted and the same
records are added there.Two failure modes worth knowing before you start. dnsRecords replaces the whole zone, so
adopting a domain Cargo mailboxes send from deletes their mail records and outreach stops. And a
hostname serves nothing until its _cargo-verify TXT resolves, so a successful deploy is not yet a
live site.
Build our company website on Cargo from our brand guide and serve it on www.fabrikam.example. Our DNS is hosted elsewhere.
Illustrative output, fictional records:
PR #12 Website: home, about and pricing from the brand guide merged after review
app:website deployed Routing: static
https://website-1a2b3c4d.app.getcargo.run/pricing/ Pricing | Fabrikam
www.fabrikam.example pending: _cargo-verify not resolved
DNS is hosted elsewhere, so infra/domains/website.ts was deleted. Add at the provider:
TXT _cargo-verify.www cargo-verify=9c1e4a…
CNAME _3f2a91.www _7b0c44.acm-validations.aws
CNAME www d1x2y3z4.cloudfront.net
apex fabrikam.example → https://www.fabrikam.example (provider redirect)
Once the TXT resolves, www.fabrikam.example serves the reviewed pages with their own canonical
URLs, and the next change to the site is another pull request.
This folder is a worked example: real CDK resources written for some other company. The job is
to end up with the code your company would have written, in your project, and an agent does the
adapting. If the cargo-project skill is in your session it carries the long form of this,
including CI that plans every pull request and deploys on merge; if not, this is enough.
cargo-ai cdk add cookbook/website-building writes this example to
infra/website-building/ and this procedure to .claude/skills/website-building/. No project
yet? cargo-ai cdk init <dir> --cookbook website-building && cd <dir> && npm install does
both; this folder never ships a shell. If you are reading this from the project's
.claude/skills/, the install already happened — start at step 2.agentic-engagement's) is never the website's.infra/website-building/apps/website/, and build and preview it locally.## Decisions section in your copy of this file.node --import tsx evals/contract.mjs && npm run check && cargo-ai cdk plan,
show the diff, and deploy only on an explicit yes: cargo-ai cdk deploy. A
+ create domain:… line is a non-refundable purchase, not an adopt. If the project deploys
from CI, the merged pull request is the deploy; do not also deploy from a laptop.Derive before you ask. An input with a lookup is looked up, not asked.
| Input | Kind | How it is answered | Why it matters |
|---|---|---|---|
| company, audience, offering, proof | value | derived: context/ and the existing site or brand guide; ask only for what is missing | The pages say only what the company approved. Placeholder copy and invented proof never ship. |
| pages, call to action, design direction | asked | propose a brief from the context and let the operator correct it | The brief is the scope every later pull request is reviewed against. |
| source | asked | a new site, a recreation of an authorized repository or live site, or a redesign | A supplied repository is built from; a screenshot is no substitute for it. |
domain and who holds its DNS (infra/apps/website.ts, infra/domains/website.ts) | asked | the domain, and Cargo or another provider. derived: whether mailboxes send from it, from cargo-ai mailboxManagement mailbox list | Decides between adopting into Cargo and adding records at the provider, and blocks the one adopt that breaks mail. |
Checked before moving on, not after the deploy:
context/global/design.md are agreed, and site.json carries only
approved factsnode --import tsx evals/contract.mjs passes against the adapted graphThe code is a worked example. These reshapes are expected, and the agent offers them rather than waiting to be asked. Every one costs something.
| Variation | When it is right | How | What it costs |
|---|---|---|---|
external-dns | The domain's DNS is at another provider, as for most existing company domains | Delete infra/domains/website.ts, keep domains on the app, add the records at the provider after the first deploy (domain) | Three records and an apex redirect kept by hand; Cargo cannot correct them. |
register-domain | The company wants a new domain for the website, bought through Cargo | Drop adopt: true in infra/domains/website.ts and the contract's adopt assertion | Workspace credits, not refundable; the plan's + create domain:… line is the purchase. |
source-recreation | An authorized repository or live site already exists | Port its pages and assets into the app, keeping attribution (build and review) | Dependency migration, inherited defects to separate from new ones, binary assets to adapt for a text upload. |
redesign | The current site does not fit the company it describes | Approve new tokens, structure and copy instead of matching the old pages | More design decisions, and no old page to compare against. |
more-pages | The brief needs pricing, landing or legal pages | app/<route>/page.tsx with pageMetadata, plus the route in app/sitemap.ts | Metadata and sitemap to keep current per page. |
working-form | A demo or contact form has to deliver | Post it to an approved destination and test a real submission | A backend outside this static app, with its own consent and cost. |
However far you adapt, these hold. Ask for one anyway and the agent tells you what breaks, then does
it if you still want it, and records why under ## Decisions in your copy of this file.
infra/domains/website.ts) dnsRecords
replaces the whole zone, so the MX, SPF, DKIM and DMARC records disappear and mail stops. Give
the website a dedicated domain, or leave the DNS where it is and delete the file.www, never the apex. (infra/apps/website.ts) The apex cannot
CNAME to an app; it forwards to https://www.<domain>, and site.json canonicalUrl is
https://www.<domain>/, so search engines index one origin.dnsRecords includes website.domainRecords. (infra/domains/website.ts) Without it the
zone has no _cargo-verify TXT and no www CNAME, and the hostname never verifies.infra/apps/website/next.config.ts) output: "export"
and trailingSlash: true. Without the trailing slash, /about serves the home page.infra/apps/website/site.json) status stays draft
(noindex, robots disallow) until the operator approved the pages. No invented customers,
numbers, prices or testimonials.infra/apps/website/) No Cargo token, login, private context
or .env in it: everything in the build is readable by anyone, so check dist/, not just git.node --import tsx evals/contract.mjs passes: one app in the website-building-apps folder
with a www hostname and a static-export build, and either no domain or an adopted domain whose
dnsRecords holds the app's domainRecords and whose apex forwards to wwwnpm ci && npm run check && npm run build in the app writes dist/index.html,
dist/about/index.html, dist/robots.txt and dist/sitemap.xml, each page with its own title,
description and canonical in the initial HTMLcargo-ai cdk plan shows the folder, the app and the domain (or no domain) as an adopt, not a
create, and the operator approved itRouting: static, and the Cargo URL serves /about and /about/ directlyhttps://www.<domain>/ serves the site in an anonymous session with a valid certificate, the
apex redirects to it, and pages carry it as their canonical URL; a pending TXT is reported as
pendingsite.json is ready only for approved content, and a content change reaches the site through
one reviewed pull request whose plan shows the same app with a new content hashEach merge is one deployment of a static app; read the workspace's current hosting usage before
the first deploy, and say what you found. Adopting a domain the workspace owns adds no charge.
Registering one (register-domain) charges workspace credits once and is not refundable: read
the current price and the credit balance before proposing it, and quote both with the plan. A
working form adds whatever its backend costs. Nothing in the workspace runs on a schedule; your
coding agent's usage is the rest.
call-capture (the customer language it collects is what the pages should say), account-scoring
and tam-building (the ICP they write down is who the site speaks to), agentic-engagement (its
sending domain is never the website's).