# Where the Skin code/logic lives

A source map of everything that makes Agent Canvas skins work, across the
three places it is spread over.

## Host side — [`OpenHands/OpenHands`](https://github.com/OpenHands/OpenHands), branch [`feature/skins`](https://github.com/OpenHands/OpenHands/tree/feature/skins) ([PR #16232](https://github.com/OpenHands/OpenHands/pull/16232))

The machinery that installs, runs, and integrates skins. Not yet on
`main`.

### Backend (the core of the system)

| Source | Role |
|---|---|
| [`scripts/skin-service.mjs`](https://github.com/OpenHands/OpenHands/blob/feature/skins/scripts/skin-service.mjs) | **The heart of it.** Installs the skin repo (clones to `~/workspace/skin`), validates `skin.yaml`/`package.json`/`SKILL.md`, supervises the `npm run start` process (own process group, restart with backoff), syncs `SKILL.md` into `~/.openhands/skills/` as an always-active skill, applies `automations/`, manages secrets during guided setup. Service state lives in `~/.openhands/agent-canvas/skin/` |
| [`scripts/static-server.mjs`](https://github.com/OpenHands/OpenHands/blob/feature/skins/scripts/static-server.mjs) | Mounts the skin REST API under **`/skin-api`** and reverse-proxies **`/skin`** → the running skin app on `$OPENHANDS_SKIN_PORT` (flags: `--skin-port`, `--skin-agent-server-url`, `--skin-automation-url`, `--skin-canvas-version`) |
| [`bin/agent-canvas.mjs`](https://github.com/OpenHands/OpenHands/blob/feature/skins/bin/agent-canvas.mjs) | CLI launcher that wires skin support into the stack |
| [`__tests__/skin-service.test.ts`](https://github.com/OpenHands/OpenHands/blob/feature/skins/__tests__/skin-service.test.ts), [`__tests__/bin/agent-canvas.test.ts`](https://github.com/OpenHands/OpenHands/blob/feature/skins/__tests__/bin/agent-canvas.test.ts) | Tests |

### Frontend (Canvas UI integration)

| Source | Role |
|---|---|
| [`src/api/skin-service.ts`](https://github.com/OpenHands/OpenHands/blob/feature/skins/src/api/skin-service.ts) | Client for the `/skin-api` endpoints |
| [`src/hooks/query/use-skin.ts`](https://github.com/OpenHands/OpenHands/blob/feature/skins/src/hooks/query/use-skin.ts) | Skin state query hook |
| [`src/hooks/use-skin-theme.ts`](https://github.com/OpenHands/OpenHands/blob/feature/skins/src/hooks/use-skin-theme.ts) | Applies the skin's `theme:` colors to the **whole** Canvas UI |
| [`src/routes/skin-tab.tsx`](https://github.com/OpenHands/OpenHands/blob/feature/skins/src/routes/skin-tab.tsx) | The iframe tab that embeds the skin app as the default nav item |
| [`src/routes/skin-settings.tsx`](https://github.com/OpenHands/OpenHands/blob/feature/skins/src/routes/skin-settings.tsx) | Settings → Skin page (install by repo URL, guided secret setup, restart) |
| [`src/api/agent-canvas-updates.ts`](https://github.com/OpenHands/OpenHands/blob/feature/skins/src/api/agent-canvas-updates.ts) + [`src/components/features/settings/agent-canvas-update-card.tsx`](https://github.com/OpenHands/OpenHands/blob/feature/skins/src/components/features/settings/agent-canvas-update-card.tsx) | Version/update surfacing (`canvas_version` compatibility) |

### Deployment

| Source | Role |
|---|---|
| [`helm/agent-canvas/`](https://github.com/OpenHands/OpenHands/tree/feature/skins/helm/agent-canvas) (Chart.yaml, values.yaml, templates/…) | The chart ACM fetches live to provision instances; `config.skin.repo` / `config.skin.ref` select the skin per instance |

## Marketplace — [`OpenHands/extensions`](https://github.com/OpenHands/extensions) (private)

- [`skins/marketplace.json`](https://github.com/OpenHands/extensions/blob/main/skins/marketplace.json) — the curated list of installable skins
  (repos may be private; ACM reads it with `GITHUB_TOKEN`)

## Per-skin repos — e.g. this repo (`skin-acm`)

The contract every skin implements (everything at the repo root):

| Source | Role |
|---|---|
| [`skin.yaml`](../skin.yaml) | Manifest: name, icon, theme, screenshot, `canvas_version`, secret names |
| [`package.json`](../package.json) | Must define a `start` script (→ `start-skin.py` here) |
| [`SKILL.md`](../SKILL.md) | Required; synced into agent context as an always-active skill |
| `automations/` | Optional bundled automations (ACM has none) |
| App code | Here: [`backend/server.py`](../backend/server.py) (stdlib fleet API + static serving) and [`web/index.html`](../web/index.html) (the SPA) |
| [`skin/SKILL.md`](../skin/SKILL.md) | ACM-specific: the skin-builder skill it installs on new instances |

## TL;DR

~90% of the actual skin *logic* is in one file —
[`scripts/skin-service.mjs`](https://github.com/OpenHands/OpenHands/blob/feature/skins/scripts/skin-service.mjs) on the `feature/skins` branch — with
`static-server.mjs` doing the proxying, ~6 small frontend files doing the
UI embedding/theming, and the helm chart + `marketplace.json` + per-skin
repos forming the distribution side.