# Atelier Image generation for an Officer server: a provider-shaped API over OpenAI's image endpoints, storing what it makes so every image has a durable reference — a gallery you can browse, collections you can organise, and a job an agent can poll rather than block on. Atelier was a standalone service before this plugin existed (`~/officer-dev/dockers/officer-atelier`, still running as `officer-atelier`, still what the mobile app talks to). This plugin is a fresh extraction into Officer, not a replacement for it yet — see `PLUGIN.md` before assuming the two are interchangeable. ## What you get - **Generate** — a prompt, a model, size/quality/background, and a job you watch settle. `n` images become `n` independently-tracked rows, so one failure out of four does not discard the three that worked. - **Gallery** — everything generated, newest first, with a lightbox: pan, zoom about the cursor, checkerboard/dark/light backdrops for telling a transparent background from an opaque one that only looks it. - **Collections** — named sets of images. A generation request can name one by name and it is created on first use; the gallery and the lightbox can file an image into one after the fact. - **A provider-shaped backend.** "OpenAI" is code, written once; a *provider* is a row — which model, whose key, on or off — and several can share one adapter. Today OpenAI is the only adapter; the point of the shape is that a second one is a new file, not a redesign. - **An OpenAI-compatible job API**, for agents. `POST /api/atelier/images/generations` accepts OpenAI's request body verbatim and answers `202` with a job id rather than blocking for the length of the generation — the caller polls `GET /api/atelier/images/generations/:id`, optionally with `?wait=` to long-poll instead of retrying in a loop. ## Requirements - An Officer server, version 1.0.0 or later. - A **separate Postgres database**, and its own environment variable: `ATELIER_POSTGRES_URL`. Atelier's tables (`generations`, `collections`, `collection_items`, `providers`) do not live in `officer_db` — see `PLUGIN.md` for why, and set this before installing. There is no default. - An OpenAI API key, set with `bun run secret:set atelier-openai-key`, or configured per-provider in Settings → Integrations → Atelier, or (for an install that predates the secret store) as `OPENAI_API_KEY` in `.env`. ## Install From the Officer marketplace: find Atelier, install. That mounts `/api/atelier` and `/atelier`, starts the `officer-atelier` sidecar under PM2, and connects it to whatever `ATELIER_POSTGRES_URL` points at — which has to already have the schema (the standalone repo's own `bun db:push` creates it; this plugin does not run migrations against a database it does not own the schema of). Uninstalling stops the sidecar and unmounts the route. **It never touches your database** — Atelier's tables and rows are not this plugin's to drop, since it does not own the schema push for them either. ## Permissions One permission, `atelier`, grantable per role at read or write. - **Read** lets an account use Generate, browse the Gallery and Collections, and see which providers are configured (never a key, in any form — not even masked). - **Write** additionally lets an account add, edit or delete providers, including their API keys. A provider's key authorises real spend on someone's OpenAI account, so treat write here the way you would treat write on any billing-adjacent surface. Providers is not a panel — it is a platform Settings screen (Settings → Integrations → Atelier), because it is owner-level configuration rather than a working view. See `PLUGIN.md` for why. ## Limitations, honestly - **Two Ateliers can exist at once.** This plugin and the standalone service are independent — installing this one does not migrate the standalone one's data, stop it, or repoint the mobile app. See `PLUGIN.md`. - **No per-user data.** Every account with `read` sees the same gallery and the same collections — there is no ownership model yet, the same simple shape offscale shipped with first. - **The database is separate from `officer_db`**, which means Atelier's tables are not covered by `bun db:push`, are not part of the platform's usual backup story unless you back up the second database too, and need their own connection string. Migrating them in is real, tracked follow-up work — see `PLUGIN.md`.