|
| 1 | +--- |
| 2 | +title: Marketplace handbook for app authors |
| 3 | +navTitle: Marketplace Handbook |
| 4 | +description: From a built dist/objectstack.json to an app a customer has installed — the listing file, the README a listing depends on, publishing and review, every screen the customer's administrator meets, and staying current with platform versions. |
| 5 | +--- |
| 6 | + |
| 7 | +This page follows one app from a built `dist/objectstack.json` to a customer |
| 8 | +environment and back out, in the order you meet each step. The mechanics it |
| 9 | +touches live on the pages it links. |
| 10 | + |
| 11 | +## What a listing is |
| 12 | + |
| 13 | +A Marketplace listing is an **App** listing: the customer downloads, opens and |
| 14 | +uninstalls exactly one App, and only a `type: 'app'` package is |
| 15 | +consumer-installable |
| 16 | +([ADR-0019](https://github.com/objectstack-ai/objectstack/blob/main/docs/adr/0019-app-as-consumer-unit.md) |
| 17 | +D1/D2). Modules, plugins and other contributions ship **inside** that App — see |
| 18 | +[Extras](#extras-dependencies-and-modules) below. |
| 19 | + |
| 20 | +The listing is a package row in the catalog plus its versions: the row carries |
| 21 | +the name, description, category, icon, README and translations, and each |
| 22 | +version is a snapshot of your compiled artifact. |
| 23 | + |
| 24 | +## The listing file: `objectstack.manifest.json` |
| 25 | + |
| 26 | +`os package publish` reads `objectstack.manifest.json` from the directory you |
| 27 | +run it in, if one is there, and uses it as the default for the listing. **A |
| 28 | +command-line flag always wins** over the file. The file's full schema is the |
| 29 | +generated [Template Manifest reference](/docs/references/marketplace/template-manifest); |
| 30 | +the declared keys below are the ones publish actually sends. |
| 31 | + |
| 32 | +| Key | What it fills | Flag that overrides it | |
| 33 | +|:---|:---|:---| |
| 34 | +| `manifestId` | The package id (reverse-domain, immutable once published) | `--manifest-id` | |
| 35 | +| `displayName` | The name on the card | `--display-name` | |
| 36 | +| `description` | The short description | `--description` | |
| 37 | +| `category` | The catalog category slug (`crm`, `hr`, `finance`, …) | `--category` | |
| 38 | +| `iconUrl` | A public http(s) icon URL | `--icon-url`, or `--icon-file` to upload one | |
| 39 | +| `homepageUrl` | Your project or docs URL | `--homepage-url` | |
| 40 | +| `license` | An SPDX identifier | `--license` | |
| 41 | +| `readmePath` | A markdown file, relative to the manifest, read at publish time | `--readme-file`, `--readme` | |
| 42 | +| `translations` | Per-locale `displayName`, `description`, `tagline`, `readme` and `screenshotCaptions` | — | |
| 43 | + |
| 44 | +[HotCRM](https://github.com/objectstack-ai/hotcrm/blob/main/objectstack.manifest.json) |
| 45 | +is a worked example for the keys it carries: |
| 46 | + |
| 47 | +```json |
| 48 | +{ |
| 49 | + "manifestId": "app.objectstack.hotcrm", |
| 50 | + "displayName": "HotCRM", |
| 51 | + "category": "crm", |
| 52 | + "license": "Apache-2.0", |
| 53 | + "iconUrl": "https://raw.githubusercontent.com/objectstack-ai/hotcrm/main/assets/icon.svg", |
| 54 | + "homepageUrl": "https://github.com/objectstack-ai/hotcrm", |
| 55 | + "readmePath": "README.md" |
| 56 | +} |
| 57 | +``` |
| 58 | + |
| 59 | +HotCRM carries no `translations`. A listing in two locales adds one entry per |
| 60 | +locale; a `readme` entry may be inline markdown or a path to a `.md` file next |
| 61 | +to the manifest: |
| 62 | + |
| 63 | +```json |
| 64 | +{ |
| 65 | + "translations": { |
| 66 | + "en": { "tagline": "Repair tickets, end to end.", "readme": "README.md" }, |
| 67 | + "zh-CN": { |
| 68 | + "displayName": "维修工单", |
| 69 | + "description": "从报修到结单的维修工单应用。", |
| 70 | + "tagline": "报修到结单,一个应用。", |
| 71 | + "readme": "README.zh-CN.md" |
| 72 | + } |
| 73 | + } |
| 74 | +} |
| 75 | +``` |
| 76 | + |
| 77 | +The Console picks the entry for the viewer's language — the exact locale, then |
| 78 | +the bare language (`zh` reaches `zh-CN`), then `en` — and falls back to the base |
| 79 | +columns for a missing key. The per-locale shape is the |
| 80 | +[`translations` entry of CreatePackageRequest](/docs/references/marketplace/package). |
| 81 | + |
| 82 | +<Callout type="warn"> |
| 83 | +**Two things the file can say that never reach the listing.** Publish uploads no |
| 84 | +screenshot images, and the Console's marketplace pages render none: a |
| 85 | +`preview.screenshots` list stays on your disk, and `screenshotCaptions` travels |
| 86 | +with no image to caption. The schema also accepts `tags`, `publisher` and |
| 87 | +`isStarter`, but `os package publish` does not send them. Put the icon, the |
| 88 | +description, the tagline and the README where the table above says, and nothing |
| 89 | +else. |
| 90 | +</Callout> |
| 91 | + |
| 92 | +## The README the listing depends on |
| 93 | + |
| 94 | +The README is the long description on the app's marketplace page, and the |
| 95 | +customer's administrator reads it before installing. Publish **never** picks up a |
| 96 | +`README.md` on its own — it sends one only from `readmePath`, `--readme-file` or |
| 97 | +`--readme`, so development notes do not leak into the catalog. Write it for the |
| 98 | +administrator, not for a contributor: |
| 99 | + |
| 100 | +- **What the app does, in business words** — the objects and the work they |
| 101 | + support, not the folder layout. |
| 102 | +- **The sample data and any demo users** — what installing *with sample data* |
| 103 | + adds (your [seed datasets](/docs/data-modeling/seed-data)), and what an empty |
| 104 | + install looks like. |
| 105 | +- **Every credential it needs** — for each one, its name, where the |
| 106 | + administrator obtains it, and which permissions or scopes it must carry. You |
| 107 | + write the names; the administrator supplies the values. The platform does not |
| 108 | + prompt for credentials at install yet: the install-time prompt and the |
| 109 | + credential store arrive with |
| 110 | + [#27237](https://github.com/objectstack-ai/objectstack/issues/27237) and the |
| 111 | + Console credentials page with |
| 112 | + [#27238](https://github.com/objectstack-ai/objectstack/issues/27238). Until |
| 113 | + then the README is the whole hand-off. An app that ships declarative |
| 114 | + connectors gains an offline spec-normalization tool with |
| 115 | + [#26709](https://github.com/objectstack-ai/objectstack/issues/26709). |
| 116 | + |
| 117 | +## Build and verify before publishing |
| 118 | + |
| 119 | +A scaffolded project carries the scripts; run them before every publish: |
| 120 | + |
| 121 | +```bash |
| 122 | +npm run validate # os validate — the author-time rules |
| 123 | +npm run lint # os lint |
| 124 | +npm run typecheck # tsc --noEmit |
| 125 | +npm test # vitest run |
| 126 | +npx os verify # boots the app in-process and proves it through the real HTTP stack |
| 127 | +npm run build # os build -> dist/objectstack.json |
| 128 | +``` |
| 129 | + |
| 130 | +Two declarations name the platform major your app is authored against: |
| 131 | +`engines.protocol` in the `defineStack` manifest, and `specVersion` in |
| 132 | +`objectstack.manifest.json`. A runtime outside the `engines.protocol` range |
| 133 | +refuses the app rather than half-loading it. See |
| 134 | +[Validating metadata](/docs/deployment/validating-metadata) for what the checks |
| 135 | +cover. |
| 136 | + |
| 137 | +## Publish and submit for review |
| 138 | + |
| 139 | +Sign in to the control plane once with `os cloud login` (it writes |
| 140 | +`~/.objectstack/cloud.json`), then publish from the directory that holds the |
| 141 | +manifest: |
| 142 | + |
| 143 | +```bash |
| 144 | +os package publish ./dist/objectstack.json \ |
| 145 | + --version 1.2.0 \ |
| 146 | + --icon-file ./assets/icon.png \ |
| 147 | + --visibility marketplace \ |
| 148 | + --submit \ |
| 149 | + --note "First marketplace release" |
| 150 | +``` |
| 151 | + |
| 152 | +The flags that shape a listing, on top of the overrides in the table above: |
| 153 | + |
| 154 | +| Flag | Effect | |
| 155 | +|:---|:---| |
| 156 | +| `--visibility` | `org`, `private` or `marketplace`. Omitted, an existing package keeps its visibility and a new one gets the control plane's default | |
| 157 | +| `--icon-file` | Uploads a PNG, JPEG, WebP or SVG (up to 256 KB) and stores the served URL as the icon. Not together with `--icon-url` | |
| 158 | +| `--readme-file` | Reads the README from that path at publish time. Not together with `--readme` | |
| 159 | +| `--pre-release` | Marks the version a pre-release; a version like `1.3.0-beta.1` is marked one anyway | |
| 160 | +| `--submit` | Submits the new version for marketplace review. Needs `marketplace` visibility and a complete listing | |
| 161 | +| `--note` | Release notes for this version | |
| 162 | + |
| 163 | +What review checks is the control plane's business. What you see is what the CLI |
| 164 | +prints: when a listing is incomplete — the README and the icon are required for |
| 165 | +a marketplace listing unless already stored on the package — the publish |
| 166 | +answers with a list of **Marketplace policy violations**; fix them and publish |
| 167 | +again. On success it prints the version's listing status: |
| 168 | + |
| 169 | +| Status | What it means for you | |
| 170 | +|:---|:---| |
| 171 | +| `draft` | Not submitted. With `marketplace` visibility the CLI reminds you to pass `--submit` | |
| 172 | +| `pending_review` | Waiting for a platform admin to approve or reject it | |
| 173 | +| `approved` | Live in the public catalog | |
| 174 | + |
| 175 | +`--auto-approve` skips the queue for platform admins only. Every other flag, the |
| 176 | +service-mode options and the publish-and-install shortcut are on |
| 177 | +[Publish, versioning and preview](/docs/deployment/publish-and-preview#2-publish-a-package-version). |
| 178 | + |
| 179 | +## The customer's side |
| 180 | + |
| 181 | +What the administrator meets, step by step. Each step names the page that owns |
| 182 | +the detail. |
| 183 | + |
| 184 | +1. **Finding the app.** In the Console, **Setup → Browse Marketplace** opens the |
| 185 | + **App Marketplace** page, which lists the approved apps; clicking one opens |
| 186 | + its page with the README, version, category and license. A runtime started |
| 187 | + with `OS_CLOUD_URL=off` shows **App Marketplace is turned off** instead — |
| 188 | + [Cloud connection](/docs/deployment/cloud-connection) covers connecting one. |
| 189 | +2. **Installing.** Into a cloud environment, **Install to cloud…** opens a |
| 190 | + dialog that names or picks the environment and offers **Include sample |
| 191 | + data**; only organization owners and admins can install. On a runtime that installs |
| 192 | + locally, **Install** puts the app into this runtime; the result may say it |
| 193 | + loads at the next restart. Without a catalog at all, the operator runs |
| 194 | + `os package install ./dist/objectstack.json` against the runtime |
| 195 | + ([Publish, versioning and preview](/docs/deployment/publish-and-preview#3-install-a-version)). |
| 196 | +3. **Choosing an install mode**, when your manifest permits more than one: |
| 197 | + **Managed** keeps the package the authority and receives upgrades; an |
| 198 | + **Unmanaged copy** is editable and receives none |
| 199 | + ([Install modes](/docs/deployment/install-modes)). |
| 200 | +4. **The dependency check.** An app that declares a dependency the environment |
| 201 | + lacks is refused before anything is written, one line per unmet entry |
| 202 | + ([App dependencies](/docs/deployment/app-dependencies)). |
| 203 | +5. **Credentials.** No screen yet — the administrator follows your README (see |
| 204 | + above, and #27237 / #27238). |
| 205 | +6. **Sample data.** The **⋯** (More install options) menu on the app's page |
| 206 | + offers **Add sample data**, then **Re-seed sample data**, and **Purge sample |
| 207 | + data**, which deletes only the records your seed created. Behind them are |
| 208 | + `POST /api/v1/marketplace/install-local/:manifestId/reseed-sample-data` and |
| 209 | + `…/purge-sample-data`, both requiring `manage_metadata`. |
| 210 | +7. **Enable and disable.** **Setup → Installed Apps** lists the apps installed |
| 211 | + from the marketplace. **Disable** is offered only for a writable package: |
| 212 | + for a package provided by code or by an installed app, the runtime answers |
| 213 | + `PATCH /api/v1/packages/:id/disable` with `422 WRITABLE_PACKAGE_REQUIRED`. |
| 214 | + To take an installed app away, uninstall it. |
| 215 | +8. **Upgrading.** You publish a new version; nothing running changes. An |
| 216 | + environment installed through the cloud shows **Update available → v…** and |
| 217 | + an **Update** button; a runtime that installs locally shows **Reinstall**. A |
| 218 | + managed install takes the upgrade; an unmanaged copy does not. To roll back, |
| 219 | + install the earlier version. |
| 220 | +9. **Uninstalling.** **Uninstall from this runtime** in the **⋯** menu, or |
| 221 | + `DELETE /api/v1/marketplace/install-local/:manifestId` (`manage_metadata`). |
| 222 | + There is no CLI command. It removes the cached manifest, withdraws the app |
| 223 | + from the running kernel and revokes its permission sets and their grants. The |
| 224 | + answer's `note` and `cleanups` say whether any part stays loaded until the |
| 225 | + next restart. **Records stored in the app's objects stay in the database.** |
| 226 | + |
| 227 | +## Staying current with platform versions |
| 228 | + |
| 229 | +The platform ships on its own release train; your app moves when you move it. |
| 230 | +Metadata authored one major behind still loads, with conversion notices in the |
| 231 | +boot log; two or more majors behind, the load rejects the retired shapes. An |
| 232 | +installed app whose `engines.protocol` the runtime does not support is not |
| 233 | +loaded, and the app's page shows **Not loaded** with the protocol range it |
| 234 | +targets. |
| 235 | + |
| 236 | +To move, run `os migrate meta --from <the major you authored against>` on your |
| 237 | +source, apply what it lists, update `engines.protocol` and `specVersion`, |
| 238 | +rebuild, and publish a new version. [Upgrading](/docs/upgrading#the-metadata-app) |
| 239 | +owns the loop. |
| 240 | + |
| 241 | +## Extras: dependencies and modules |
| 242 | + |
| 243 | +An app ships extras in one of two shapes: |
| 244 | + |
| 245 | +- **A separate app it depends on** — a region pack, a shared base app. Declare it |
| 246 | + in `manifest.dependencies`; install checks it, and the customer installs it |
| 247 | + first ([App dependencies](/docs/deployment/app-dependencies)). |
| 248 | +- **A module built into the same artifact** — a `type: 'module'` package |
| 249 | + composed at build time, which travels inside your App rather than as a |
| 250 | + listing of its own ([A project is a multi-package artifact](/docs/getting-started/examples#a-project-is-a-multi-package-artifact)). |
| 251 | + |
| 252 | +## Related |
| 253 | + |
| 254 | +- [CLI reference](/docs/deployment/cli) — every `os package publish` and `os package install` flag |
| 255 | +- [Marketplace protocol reference](/docs/references/marketplace/marketplace) |
0 commit comments