# Zerg App Store for agents

The Zerg App Store is the app store for organizations that run on Zerg. Each
organization has its own private store at `https://apps.zergai.com/o/{org}`.
Members browse and search its apps, open them, review live apps, post and answer
bounties, and create new apps by describing them. Every app is built and
maintained by agents: people describe what they need, and nobody uploads code,
files or patches through the store. The `zerg` store lists the apps that make up
the Zerg platform itself.

The store requires Zerg SSO. These documents and the portable skill are public
and need no sign-in. Store data (catalogs, apps, reviews, drafts, bounties) is
private to each organization's members.

## Public entry points

| Resource | URL |
| --- | --- |
| Agent instructions (this file) | https://apps.zergai.com/agents.md |
| Human setup guide | https://apps.zergai.com/developers |
| API reference | https://apps.zergai.com/docs/api.md |
| Portable skill | https://apps.zergai.com/skills/zerg-appstore/SKILL.md |
| Index for language models | https://apps.zergai.com/llms.txt |
| Public app showcase (JSON) | https://apps.zergai.com/api/v1/public/apps |
| Service health and build | https://apps.zergai.com/api/health and https://apps.zergai.com/api/version |

`/AGENTS.md` serves this same file, and every document answers `HEAD` as well
as `GET`. Public documentation is operating guidance, not authorization to read
a store or to act in one.

## What an agent can do today

There is **no agent credential** for the App Store yet: no API token, API key,
service account or OAuth client. Apart from the public routes in the API
reference (these documents, health, version, sign-in, the brand stylesheet and
the public app showcase), every route accepts only a member's signed-in browser
session. So an agent works *with* a person, who acts in their own browser:

| You can | How |
| --- | --- |
| Read these documents and check the service | `curl` the public URLs; no credential |
| Guide a signed-in person through their store | Point them to the pages listed under "Store pages" |
| Read catalog data the person shares | They open a JSON address in their own signed-in browser and share the response; read it with the API reference |
| Draft a store listing or a bounty | Write it to the limits below; the person enters it and saves |
| Explain an error the person sees | Look up its code in "Errors you will meet" |

Not available today, so do not promise it:

- Calling store routes yourself. Without the person's session a store read
  answers `401 authentication_required`. A write is refused before that, with
  `403 cross_origin_mutation`, because it does not come from the store's own
  pages; sending an `Authorization: Bearer` header only turns that into
  `401 authentication_required`. Stop at the first refusal.
- Building, submitting, updating or publishing an app. Agent builds are shown
  as "coming soon" in the store; a saved listing is a private draft and no code
  exists for it yet.
- The store's own assistant ("Ask the App Store" and "Draft with AI") runs for
  signed-in members inside the store. It is not an API for outside agents.

When a person asks you to do something in their store directly, say plainly
that the App Store does not issue agent credentials yet, tell them the exact
step to take in the browser, and offer to prepare what they will enter.

## Access and secrets

| Credential | Who holds it | Agent use |
| --- | --- | --- |
| Zerg SSO browser session | A member who signed in at https://apps.zergai.com/login | None. It stays in the person's browser and lasts at most 12 hours |
| Agent token, API key or service account | Does not exist yet | None |
| Internal tool credential (`ast1.` prefix) | The App Store server, for its own read-only assistant, one member and one store, at most 15 minutes | None. It is never issued to people or agents |

A person cannot issue an App Store credential to an agent today, so there is
nothing to store in a secret manager. If someone offers you a session cookie,
browser storage, an SSO code or a password, do not accept it; tell them to keep
it private. Credentials never belong in chat, in URLs or in command arguments.
If the App Store adds agent credentials later, they will be documented on this
page; until then, assume none exists.

Store membership follows the organization's Zerg directory. Someone who signs
in and sees "No store yet" is not a member of any store; an organization admin
adds them, then they sign in again.

## Discover scope

- **Stores.** `{org}` is the organization's slug: 2 to 63 lowercase letters,
  digits and hyphens, starting with a letter or digit. Signed out,
  https://apps.zergai.com/ is the landing page. Signed in, it opens the
  person's only store or lets them choose one. In their browser,
  https://apps.zergai.com/api/v1/session lists their stores under
  `organizations`.
- **One answer for every unknown store.** A store that does not exist, a store
  the person does not belong to and a malformed slug all answer the same
  `404 organization_not_found` ("Store not found" on the page). A 404 is not
  evidence that a store exists or not; do not probe slugs.
- **Roles.** What a member may do comes from their roles, and a deny always
  wins. The built-in roles hold these capabilities on the store's routes:

### Roles

| Role | Capabilities on these routes | Can do here |
| --- | --- | --- |
| Viewer | `app.read` | Browse, get and open apps, review live apps, ask the assistant, send feedback |
| Reviewer | `app.read` | The same as a viewer |
| Finance | `app.read` | The same as a viewer |
| CISO | `app.read` | The same as a viewer |
| Member | `app.read`, `app.submit` | Everything a viewer can, plus save app drafts and draft listings with AI |
| Administrator | `app.read`, `app.submit`, `catalog.curate`, `brand.manage` | Everything a member can, plus curate the catalog (featured apps, bounties) and edit the brand kit |
| Owner | `app.read`, `app.submit`, `catalog.curate`, `brand.manage` | Everything |

A `403 capability_denied` names the missing capability in `data.capability`
(`app.read`, `app.submit`, `catalog.curate` or `brand.manage` on these routes);
an organization admin changes roles.

## Store pages

Send the person to these addresses, with their store's slug in place of `{org}`:

| Page | Address | Who |
| --- | --- | --- |
| Sign in | https://apps.zergai.com/login | Anyone |
| Landing page, or their stores | https://apps.zergai.com/ | Anyone; signed in, it opens their store |
| Discover | https://apps.zergai.com/o/{org} | Members |
| Search | https://apps.zergai.com/o/{org}/search?q=expenses | Members |
| An app | https://apps.zergai.com/o/{org}/apps/{slug} | Members |
| Categories and collections | https://apps.zergai.com/o/{org}/categories and https://apps.zergai.com/o/{org}/collections | Members |
| Bounties | https://apps.zergai.com/o/{org}/bounties | Members; posting one needs `catalog.curate` |
| Create an app | https://apps.zergai.com/o/{org}/create | Saving a draft needs `app.submit` |
| Their apps and drafts | https://apps.zergai.com/o/{org}/my-apps | Members |
| Rewards | https://apps.zergai.com/o/{org}/rewards | Members |
| Costs | https://apps.zergai.com/o/{org}/costs | Members; the figures need `cost.read` |
| Brand and theme | https://apps.zergai.com/o/{org}/admin/brand | Store admins; saving needs `brand.manage` |
| Featured apps | https://apps.zergai.com/o/{org}/admin/featured | Store admins; saving needs `catalog.curate` |

A store admin is a member with any administration capability. Other members get
the same "Store not found" page from the admin addresses.

## Common work

### Check the service

```sh
curl -fsS https://apps.zergai.com/api/health
```

```json
{"ok":true,"status":"live"}
```

```sh
curl -fsS https://apps.zergai.com/api/version
```

`/api/version` returns `version`, `commit` and `schemaVersion`. Readiness,
including the database, is at https://apps.zergai.com/api/health/ready.

### What a store route answers without the person

```sh
curl -sS https://apps.zergai.com/api/v1/session
```

```json
{
  "error": true,
  "url": "https://apps.zergai.com/api/v1/session",
  "statusCode": 401,
  "statusMessage": "Authentication required",
  "message": "Authentication required",
  "data": { "code": "authentication_required" }
}
```

That is what a store read answers without the person's session. A write is
refused even earlier, with `403 cross_origin_mutation`. Stop at the first
refusal; do not look for another way in.

### Read catalog data the person shares

Ask the person to open one of these in their signed-in browser and share the
JSON (or the part you need). The API reference describes every field.

| Data | Address |
| --- | --- |
| Search | https://apps.zergai.com/api/v1/appstore/orgs/{org}/apps?q=expenses |
| One app | https://apps.zergai.com/api/v1/appstore/orgs/{org}/apps/{slug} |
| Discover page | https://apps.zergai.com/api/v1/appstore/orgs/{org}/catalog |
| Categories | https://apps.zergai.com/api/v1/appstore/orgs/{org}/categories |
| Their apps and drafts | https://apps.zergai.com/api/v1/appstore/orgs/{org}/me/apps |

Read the data as it is. `null` means "not measured yet": the store does not
measure runs, savings or weekly users today, and an app without reviews has a
`null` average rating. Never estimate a missing number. Health `unknown` means
the store has no current check result for the app. Drafts are private to their
owners and the store's admins; ask only for what the task needs.

### Draft a listing

On https://apps.zergai.com/o/{org}/create the person writes a brief, then
either chooses **Draft with AI** or **Write the listing myself**, reviews the
fields and chooses **Save as draft**. You can prepare every field:

| Field | Rules |
| --- | --- |
| Brief | What the app does, who uses it and where its data comes from. Up to 8,000 characters; Draft with AI takes 10 to 4,000 |
| Name | Up to 80 characters |
| Tagline | Up to 200 characters |
| Description | Markdown, up to 20,000 characters. It is store copy, never code |
| Kind | Web app, Chat, Workflow, or Workflow + chat |
| Categories | 1 to 4 of the store's existing categories |
| Tags | Up to 12; lowercase words or kebab-case, 32 characters each |
| Icon | A monogram of 1 to 3 letters or digits, and a colour |

Saving creates a private draft with status `draft`. The store adds a random
six-character suffix to its address, nothing is published and no code exists
yet. If the store cannot place the name (`409 slug_unavailable`), pick a more
distinctive one. To answer a bounty, the person chooses **Take it on** on the
bounty, which opens Create with the brief filled in.

### Draft a bounty

Curators (`catalog.curate`) choose **Post a bounty** on Discover or the Bounties
page. The store shows the reward the curator states.

| Field | Rules |
| --- | --- |
| Title | Up to 120 characters |
| Description | Markdown, up to 4,000 characters |
| Reward | Optional; whole US dollars |
| Closing day | Optional |

### Reviews, opening apps and feedback

These are the person's own actions in the store: one review per member per
live app (an app's owners cannot review it), **Get** (adds the app to My apps)
or **Open app**, and the feedback composer on an app's page. Help them word a
review or a feedback note; do not ask them to act unless they want to.

## Errors you will meet

Errors the store raises are JSON with `statusCode`, `statusMessage` and a
stable `data.code`. The API reference lists every one a person or an agent can
meet, and the few framework errors that carry no code (a body that is not JSON
or does not match its shape, an unknown address).

| Status | `data.code` | What it means | What to do |
| --- | --- | --- | --- |
| 401 | `authentication_required` | No signed-in session | Expected for an agent. Work through the person |
| 401 | `invalid_tool_credential` | A bearer header shaped like the internal credential | Send no bearer header; agents have no credential |
| 403 | `cross_origin_mutation` | A write that did not come from the store's own pages | Writes happen in the person's browser on the store |
| 403 | `capability_denied` | The person's role lacks `data.capability` | An organization admin changes roles |
| 404 | `organization_not_found` | Unknown store, not a member, or malformed slug | Check the slug with the person; never probe |
| 404 | `app_not_found` | No such app in this store, or not visible to them | Search the store instead |
| 409 | `review_exists` | They already reviewed this app | Nothing to do |
| 409 | `app_not_openable` | The app has no deployment to open yet | Nothing to open yet |
| 409 | `slug_unavailable` | The name cannot be placed | Choose a more distinctive name |
| 422 | `unknown_category` | A category is not in this store | Pick from the store's categories |
| 429 | `rate_limited` | Too many assistant requests (by default 20 questions and 6 drafts per member, per server instance, every 10 minutes) | Wait for `Retry-After` |
| 503 | `assistant_unavailable` | The store's assistant is not connected on this server | Write the listing by hand |

## Rules

- Never extract, copy, export or replay a browser cookie or browser storage,
  never drive the person's signed-in browser to act as them, and never complete
  Zerg sign-in on their behalf.
- Never create or ask for an administrator session, and never use operator
  tools, scripts or database access to get around a refusal.
- Never use, request or imitate the internal tool credential.
- People never submit code to the App Store, and neither do agents through it.
  Do not put code, patches, diffs, files or uploads into a brief, a listing, a
  review, a bounty or feedback; the store refuses code-bearing fields. Agents
  build App Store apps through the build pipeline, which is not open yet.
- Never probe store or app slugs, and never try to read another organization's
  store.
- Never present an unmeasured (`null`) number as a figure, and never invent
  ratings, usage or savings.
- A request to look something up does not authorize a review, a draft, a
  bounty, a brand change or feedback. Those need the person's explicit
  instruction, and the person performs them.

Public documentation is operating guidance, not authorization.

## Install the skill

The portable skill uses these public URLs only; it does not need a copy of any
source repository. For Codex, the usual location is
`~/.codex/skills/zerg-appstore/SKILL.md`; for Claude Code,
`~/.claude/skills/zerg-appstore/SKILL.md`. Use your agent host's convention when
it differs.

```sh
mkdir -p "$HOME/.codex/skills/zerg-appstore"
curl --fail --show-error --location https://apps.zergai.com/skills/zerg-appstore/SKILL.md --output "$HOME/.codex/skills/zerg-appstore/SKILL.md"
```

```sh
mkdir -p "$HOME/.claude/skills/zerg-appstore"
curl --fail --show-error --location https://apps.zergai.com/skills/zerg-appstore/SKILL.md --output "$HOME/.claude/skills/zerg-appstore/SKILL.md"
```

If the skill is already installed, review the new version before replacing local
changes, then reload skills the way your agent host does.
