Skip to content

Atelier — Build

Deploy apps you author onto an Atelier platform. The platform does not write code (as of v1.0.0 of this skill): you bring source + a Dockerfile, Atelier builds and hosts it. For inspecting/operating apps that already run, use the atelier-operate skill.

Staying current: to check whether a newer version of this skill exists without downloading the whole bundle, GET https://tryatelier.blob.core.windows.net/tryatelier/latest/skills-version.json and compare its skills.atelier-build to this file’s version:.

Setup

  • ATELIER_API_URL — base URL of the instance. Choose the address by where the agent runs:
    • In-cluster (the agent runs as an Atelier app, e.g. Hermes): use the internal service address http://atelier-core.atelier.svc.cluster.local:8080. Cluster DNS resolves it directly — no hosts-file or ingress changes, and the portal hostname (atelier.home.arpa) does not resolve inside the cluster.
    • External (e.g. Claude Desktop on your machine): a hostname/IP that resolves on that machine — e.g. http://atelier.home.arpa (add it to that machine’s hosts file / DNS) or the node IP directly.
  • ATELIER_API_TOKEN — a persistent API token sent as a bearer header. Mint one in the Atelier UI: Settings → System → API Tokens (admin-only page). Authoring needs a Developer-role token. Tokens start with atl_.
Terminal window
curl -s "$ATELIER_API_URL/api/apps" -H "Authorization: Bearer $ATELIER_API_TOKEN"

401 = token missing/revoked/expired. 403 = the token’s role is below Developer. JSON bodies require Content-Type: application/json.

Building apps

There is one way to get an app built on Atelier: bring your own code with a Dockerfile, push it, and the platform builds + deploys it. Full walkthrough in bring-your-own-code.md — scaffold + git push, Dockerfile requirements, the Next.js cookbook, /data persistence, and running a database (two patterns, with a decision rule).

By default the platform infers your app’s shape from its Dockerfile(s): one at the root → a single service; one per subdirectory → one service each. That covers most apps and needs no config file.

When you need more than Dockerfiles can say — a database alongside your app, a persistent volume at a specific path, environment the app requires — commit an atelier-spec.yaml. See atelier-spec.md. It is the only declarative file you should write; the platform still owns all Kubernetes YAML (see the rule below about not hand-writing manifests — the spec is the sanctioned input to it, not an exception).

Two convenience entry points wrap the same path:

EntryMethod & pathNotes
Deploy an existing imagePOST /api/apps/import{"name", "image", "port"} — no build at all
Clone a Git repoPOST /api/apps/clone{"name", "git_url", "branch?", "credentials?", "clone_only?"} — the platform clones, commits the source to the app’s Gitea repo, and (by default) runs a direct build. The repo must contain a Dockerfile unless clone_only is set. For a private repo pass credentials: {"token", "username?"} (used once, never stored; HTTPS only).

Import without building (clone_only: true): clone + commit the source, then stop — the app lands in a scaffolded state with a push-to-build webhook and no Dockerfile is required. Use this to bring an existing repo in to iterate on (add a Dockerfile, then git push to build), or to import a repo that has no Dockerfile yet. The first build runs on the next push.

There is no generate-from-description endpoint. POST /api/apps with a description, /update with an instruction, /plan, /answer, and /fix were removed when the platform stopped authoring code. If the user gives you an app idea rather than code: you write the code (you’re the agent), then land it via the bring-your-own-code path.

Push build modes

Every push-to-build webhook runs direct mode: the platform builds your committed Dockerfile(s) as-is and deploys. There are no other modes. PUT /api/apps/{name}/webhook-mode accepts only {"mode": "direct"} and exists for forward-compatibility.

Watching build progress

Default to the cheap signal — do not stream the whole build log into your context. A full build transcript (BuildKit + npm/cargo/pip output) is hundreds to thousands of lines; pulling it in on every poll is the single biggest waste of tokens when driving Atelier. Watch the small status signal to know it worked, and only read logs when something actually fails.

To know a build succeeded, poll GET /api/apps/{name} (a tiny JSON body) and watch three fields:

  • readytrue once the rollout is healthy. This is your success signal.
  • deployed_source_sha — the source commit the running build was built from. Compare it to the commit you pushed to confirm your push went live. Do not use version for this: version is repo HEAD, which the platform’s own post-build commit moves past, so it never equals your push.
  • deployed_build_id — the build that produced the running deployment.

Poll that every few seconds until ready flips true (or an error surfaces).

To diagnose a failure, fetch a bounded, diagnostic slice — never the whole log. The build-logs endpoint takes two filters:

Terminal window
# last 50 entries, error/warn/buildkit output only
curl -s "$ATELIER_API_URL/api/apps/$NAME/build-logs?level=error&tail=50" \
-H "Authorization: Bearer $ATELIER_API_TOKEN"
  • level=error returns only the kinds that explain a failure (error, warn, buildkit_stderr).
  • tail=N returns only the last N entries.
  • Fetch the unfiltered GET /api/apps/{name}/build-logs only if the bounded slice genuinely isn’t enough — usually it is.

Per-build detail (incl. source_sha) is at GET /api/apps/{name}/builds and GET /api/apps/{name}/builds/{id}.

If you must watch live, GET /api/apps/{name}/events is a Server-Sent Events stream of progress / log / done / error / question events (consume with curl -N). Watch for the terminal done / error events; do not echo every log frame into your context. For unattended builds, polling status (above) is cheaper and simpler than holding a stream open.

Lint findings appear in the build log. Atelier runs a lint gate (ruff / eslint / clippy / golangci-lint, by detected language) in the Review stage of every build. If your stack has a supported linter, the findings themselves are written to the build log verbatim as ordinary log entries — read them with ?tail=N (a plain ?level=error filters them out; it only surfaces a gate that actually blocked). Under the Hardened profile a finding blocks the deploy (the build ends in error with a “Lint gate held the deploy” message — that summary does show under ?level=error) until you push a fix or an operator overrides; under Standard it’s informational and the deploy proceeds. If no supported language is detected the log says Lint: no lintable languages detected — lint was skipped, not passed.

CVE scan results also land in the build log — but later. After a successful build Atelier runs a Trivy vulnerability scan on the built image(s). It runs post-deploy and asynchronously, so it finishes after the done event: poll GET /api/apps/{name}/build-logs?tail=20 for a few seconds past deploy and you’ll see a line like:

Scan: 0 critical, 3 high CVE(s). Full detail: GET /api/apps/{name}/builds/{id}/scan
  • GET /api/apps/{name}/builds/{build_id}/scan returns the full per-CVE detail (id, severity, package, fixed version) — fetch it to decide what to bump.
  • Under the Hardened profile, a critical CVE holds the deploy (the scan line is logged at error level with a “deploy held by the scan block” note); fix the critical CVEs and redeploy, or an operator overrides via DELETE /api/apps/{name}/scan-block.
  • A clean scan logs Scan: 0 critical, 0 high CVE(s).

LLM code review (source-level) also lands after deploy. If the platform has a language model configured, Atelier runs an AI security review of your source after each build — catching things a CVE scanner can’t: missing auth, hardcoded secrets, injection, insecure defaults. It’s async/post-deploy like the scan, and drops a build-log breadcrumb:

Code review complete — full report: GET /api/apps/{name}/builds/{id}/review
  • GET /api/apps/{name}/builds/{build_id}/review returns JSON (findings is the review as markdown, plus reviewed_at). Read it and address what it raises.
  • POST to the same path re-runs the review on demand (e.g. after you push a fix).
  • It’s complementary to the CVE scan: review = your code, scan = the image’s packages. If the platform has no LLM configured, the review simply doesn’t run (no breadcrumb) — nothing breaks.

Secrets (env vars injected into your container)

App secrets set via PUT /api/apps/{name}/secrets are surfaced as environment variables inside every container of the app. The body shape is a nested object under a top-level secrets key — not a flat {key, value} pair:

Terminal window
curl -X PUT "$ATELIER_API_URL/api/apps/$NAME/secrets" \
-H "Authorization: Bearer $ATELIER_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"secrets": {
"DATABASE_URL": "postgres://…",
"STRIPE_API_KEY": "sk_live_…"
}
}'

Returns 204 No Content. Set multiple secrets in one call. The endpoint is PUT (not POST), and the write is a merge/upsert — keys you send are created or overwritten, and keys you don’t send are left untouched (a partial PUT never wipes the rest). Sending an empty value for a key deletes it; or remove keys explicitly with DELETE /api/apps/{name}/secrets and body {"keys": ["K1","K2"]}.

List the current keys (values are never returned) with GET /api/apps/{name}/secrets.

Exposing the app publicly (after build)

Apps you build are running in-cluster and reachable from the portal hostname, but not publicly by default. If the user wants their app live on the internet at a hostname they own (e.g. blog.example.com), the platform’s Public Access feature handles it via a Cloudflare Tunnel — see the atelier-operate skill’s Public access section for the PUT /api/apps/{name}/public-access endpoint and the platform prerequisites (tunnel credentials + Cloudflare API token in Settings → System).

When the build completes, mention public exposure as an option if the user hasn’t already asked for it — particularly for blog / file-share / dashboard shapes where a public URL is the obvious next step. The operate skill drives it; this skill is just the heads-up.

Safety

  • Building consumes LLM budget and cluster resources; update rebuilds and redeploys the app. Confirm intent before kicking off builds on the user’s behalf.
  • A token only grants its role’s access; authoring needs Developer. A 403 means you need a higher-scoped token (mint one in the UI as an Admin).
  • Read the app’s real name from GET /api/apps — names often carry a suffix.