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.jsonand compare itsskills.atelier-buildto this file’sversion:.
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.
- In-cluster (the agent runs as an Atelier app, e.g. Hermes): use the
internal service address
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 withatl_.
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:
| Entry | Method & path | Notes |
|---|---|---|
| Deploy an existing image | POST /api/apps/import | {"name", "image", "port"} — no build at all |
| Clone a Git repo | POST /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:
ready—trueonce 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 useversionfor this:versionis 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:
# last 50 entries, error/warn/buildkit output onlycurl -s "$ATELIER_API_URL/api/apps/$NAME/build-logs?level=error&tail=50" \ -H "Authorization: Bearer $ATELIER_API_TOKEN"level=errorreturns only the kinds that explain a failure (error,warn,buildkit_stderr).tail=Nreturns only the last N entries.- Fetch the unfiltered
GET /api/apps/{name}/build-logsonly 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}/scanGET /api/apps/{name}/builds/{build_id}/scanreturns 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
errorlevel with a “deploy held by the scan block” note); fix the critical CVEs and redeploy, or an operator overrides viaDELETE /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}/reviewGET /api/apps/{name}/builds/{build_id}/reviewreturns JSON (findingsis the review as markdown, plusreviewed_at). Read it and address what it raises.POSTto 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:
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;
updaterebuilds 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
403means you need a higher-scoped token (mint one in the UI as an Admin). - Read the app’s real
namefromGET /api/apps— names often carry a suffix.