Olan Marketplace
Apps reach production Olan servers through Olan Marketplace: automatic checks run on the package, a person reviews it, and the marketplace signs the approved version with the marketplace key.
npx olan-app login --marketplace --server https://api.example.com # once: browser sign-in, 30-day session
npx olan-app developer create --marketplace --slug acme --name "Acme Ltd" --accept-agreement # once
npx olan-app keys register --marketplace # once per computer: your development key signs test reports
npx olan-app validate --strict && npx olan-app test --report test-report.json # packs, then tests
npx olan-app publish --marketplace --test-report test-report.json # submits, attaches, waits for the checks
npx olan-app status --marketplace com.example.my-app --wait-decision
npx olan-app app set --marketplace com.example.my-app --age-rating 4+ # once: required before release
npx olan-app release --marketplace com.example.my-app 1.0.0 --percent 10 # after approval
Reviewers apply review-guidelines.md (rule numbers like 2.1 in findings and rejections). Studio does the same in the browser: sign in to your Olan web app (e.g. https://olan.example.com) with an account that has developer access (the developer grant), open <Olan web address>/tr/studio?section=publish (Studio → Yayınla): developer accounts, store settings and page, submissions, test reports, releases.
Signing in #
The marketplace trusts your Olan server: login gets a 5-minute identity token from the server (POST /api/v2/apps/marketplace/identity-token; your account needs the developer grant from an admin) and exchanges it for a developer session. There are three ways to give login your Olan sign-in:
- Browser (default).
olan-app login --marketplace --server <Olan API address>opens the browser at the server's sign-in page (OAuth 2.0 authorization code with PKCE; the endpoints come from<server>/.well-known/openid-configuration, clientolan-app-cli) and receives the answer onhttp://127.0.0.1:<random port>/callback.--no-browseronly prints the address; open it in a browser on the same computer (the redirect goes to 127.0.0.1). The CLI waits 5 minutes. - Olan CLI.
olan app login --marketplacewith the Olan CLI signed in (olan auth login) passes your access token (OLAN_TOKEN) and server. Everyolan app …command needs an Olan CLI that has theappcommand (olan app --helpworks; Olan Apps 4a release or newer); with an olderolanusenpx olan-app …and 1. - CI. Olan has no personal access tokens or API keys to create for CI, and
olandoes not print its token. CI uses a marketplace session instead: runolan-app login --marketplace --server <api>once on your computer (browser), then store the contents of~/.olan/marketplace.json(or only itstoken) as the CI secretOLAN_MARKETPLACE_SESSION; it replaces the file, lasts 30 days (sign in again and update the secret beforeexpiresAt) and is allpublish,status,app,release,keysneed.OLAN_TOKEN/--tokenis only an input tologin(an Olan access token the Olan CLI passes); CI never needs it.
The server is --server, else OLAN_SERVER, else OLAN_API_URL (the Olan CLI sets them). The session is saved in ~/.olan/marketplace.json ({ url, token, expiresAt, userRef, … }, mode 0600; on Windows restricted to your user with icacls, best effort) and lasts 30 days. No token is ever printed or logged, and the session token is not accepted on the command line.
Marketplace address, first of: --marketplace <url>, OLAN_MARKETPLACE_URL, the saved session's marketplace (publish, status, developer), the server's /.well-known/olan marketplaceUrl (when a server is known: login --server, OLAN_SERVER, OLAN_API_URL), https://marketplace.olan.app.
Developer accounts #
Apps belong to a developer account (a person or a company); you can be a member of several.
olan-app developer create --marketplace [url] --slug <slug> --name <name> [--kind person|company] --accept-agreementcreates one and makes you its owner.--slugis the developer name in app pages (a-z, 0-9 and-, 2-63 characters;olan…is reserved). Creating an account accepts the current developer agreement;--accept-agreementis required and the CLI reads the version fromGET /api/v1/developer/me.olan-app developer listshows your accounts, roles, agreement state and pending invites (invites are accepted in Studio → Yayınla).
Publishing #
- publish. A new app needs a developer account;
--developer <slug>picks one when you have several. Existing apps get a new version (versions only go up).--channel stable|beta [--percent N]releases on approval.--visibility public(default) or--visibility organization:<serverId>/<organizationId>limits a new app to one organization on one Olan server;--visibility orgfills both from the session (the server must be registered with the marketplace). Visibility is set when the app is created; later in Studio.publishwaits while the checks run (--no-waitdoes not) and exits 1 with the findings onchecks_failed.--age-rating 4+|9+|13+sets the app's age rating after uploading. - Version numbers. A
checks_failed,rejectedor withdrawn version may be submitted again under the same number (it was never signed); one that is in review, approved or released may not (version_exists), and a new version must be higher than the latest (version_not_increasing). Organization catalog uploads (publish --org) always need a new number (manifest.md). - status. Without a version: the newest. Prints the state, each check's status, the review level and reasons and
Next:with the exact command (rejections too, on stderr before the findings). Exit 0 forapproved,checking,in_review; 1 forchecks_failed,rejected,withdrawn.--wait-decisionpolls every 5 s until a decision. - Store settings.
olan-app app show --marketplace <appId>andolan-app app set --marketplace <appId> [--age-rating 4+|9+|13+] [--category <id>] [--platforms web,desktop,ios,android] [--support-url <url>] [--privacy-url <url>](developer role;PATCH /api/v1/developer/apps/:appId). Before the first release the change applies at once; for an app live in the public store it is reviewed first (202 { pendingChange }, printed as "in review"). Studio → Yayınla → the app → Mağaza sayfası is the same. --jsonprints the marketplace's answer unchanged (no{ ok, … }envelope):publish→{ app, version, createdApp, submission }(the submission as last polled; with--test-reportalsotestReport; with--age-ratingalsostoreSettings),app→{ app, pendingChange? },status→{ version, submission },keys→{ key }/{ keys },login→{ url, expiresAt, user, developers, sessionFile },developer list→{ user, developers, agreement, invites },developer create→{ developer }, errors →{ error, message, issues?, hint? }.
Releasing and rollout #
After approval (status says nextAction: release), with an owner or admin role. The app needs an age rating first (4+, 9+ or 13+; guideline 5.4): olan-app app set --marketplace <appId> --age-rating 4+, publish --marketplace --age-rating 4+, or Studio → Yayınla → the app → Mağaza sayfası; without it release fails with age_rating_required.
olan-app release --marketplace [url] <appId> <version> [--channel stable|beta] [--percent N]: publish to a channel (defaultstable, 100%).--percent 10is a staged rollout: only that share of servers get it (chosen deterministically per server).betareaches only the organizations on the app's beta list (Studio → Yayınla).olan-app rollout … --percent N: widen it (10 → 50 → 100). It never narrows (rollout_invalid); it also resumes a halted rollout.olan-app halt --marketplace <appId> <version>: no new servers get the version; installed copies stay.olan-app withdraw --marketplace <appId> <version>: take back a submission stillcheckingorin_review.
Each prints the marketplace answer with --json ({ version }, withdraw: { submission }; errors { error, message }) and exits 0 on success, 1 on failure (2: unknown option).
Signed test reports #
The marketplace cannot run your own tests (a package has no sources), so you attach their result, bound to the exact package and signed with your development key:
olan-app keys register --marketplace [--developer <slug>] [--name <name>]registers the public half of~/.olan/olan-app-dev.key.json(OLAN_APP_DEV_KEYoverrides; created on first use) with the developer account.keys listandkeys remove <fingerprint>manage them. (Without--marketplace,keys registerregisters the key on your Olan server for your development installations instead: cli.md.)olan-app test --report test-report.jsonpacks the app first (unsigned, into.olan-app/report/; a signature does not changedigest.package), runs the whole suite and writes the report. Generate it after your last source change:publishpacks the same sources to the samedigest.package; a report for another build is refused (test_report_mismatch).--package <file>binds it to a package you packed yourself (forpublish --file). Failing runs still write a report (and exit 1); reviewers see the failures.olan-app publish --marketplace --test-report test-report.jsonchecks the report locally, submits, then attaches it (PUT …/versions/:version/test-report, only whilechecking/in_review).--jsonaddstestReport({ status: verified|unregistered_key, keyFingerprint, summary, failed }).
The file is { payload, signature: { algorithm: "ed25519", publicKey, signature, keyId } }: payload is the JSON text of { version: 1, appId, appVersion, packageSha256, generatedAt, tool: { cli, runner }, summary: { passed, failed, skipped, durationMs }, tests: [{ file, name, status, durationMs?, error? }] }, signed over its UTF-8 bytes (@olan-app/schema TestReportDocument, verifyTestReport). A registered key shows as verified to reviewers, any other as unregistered_key.
Submissions #
A submission has state, reviewLevel, reviewReasons, checks (the report below), rejection ({ rule, code, notes, findings }), release, testReport, notes (reviewer notes) and nextAction. checks is null while checking, then { version: 1, passed, startedAt, finishedAt, usage: { rpcMethods, permissionsUsed, permissionsUnused, httpHosts }, checks: [{ id, status: passed|warning|failed|skipped, durationMs, findings: [{ check, code, severity, message, path?, rule?, hint? }] }] } (@olan/contracts OlanAppCheckReport), one entry per check below.
nextAction |
Meaning |
|---|---|
wait |
checking or in_review: automatic checks or a reviewer are working |
fix_and_resubmit |
checks_failed or rejected: fix the findings, publish again (same version allowed) |
release |
approved without a release: olan-app release (or Studio → Yayınla) |
none |
approved and released (on approval or with olan-app release) |
resubmit |
withdrawn: publish again |
reviewLevel is light or full. reviewReasons say why a review is full: first_version, new_permissions (permissions the last approved version did not have), unverified_developer, security_findings (a warning or error under guideline 1.x).
Automatic checks #
Every submission runs these checks on the package (the same static checks the marketplace shares with @olan-app/schema). checks.passed is false when any finding is an error; warnings go to the reviewer; info findings are notes. Each finding has check, code, severity, message, and often path, rule (the guideline, e.g. 2.1) and hint.
| Check | Looks at | Common codes |
|---|---|---|
manifest |
olan-app.json, digest, signature and package layout (the server's install validation) |
manifest.*, package.* (errors; the other checks are skipped) |
size |
Package and entry size (≤ 5 MiB) | size.package_too_large (error), size.entry_large (warning), size.summary (info) |
files |
File types in the package | files.entry_missing, files.native_binary, files.type_not_allowed, files.svg_active_content (errors), files.source_map (warning) |
permissions |
Declared permissions against olan.* calls in the bundle, in the backend (dist/server.js, since 1.5.1), during the render, and the event subscriptions and assistant tools that need them |
permissions.missing, permissions.http_missing (errors), permissions.unused, permissions.http_unused (warnings), permissions.http_hosts (info) |
forbidden-apis |
Code the sandbox forbids (guideline 1.x) | forbidden.eval, forbidden.function_constructor, forbidden.dynamic_import, forbidden.fetch, forbidden.xhr, forbidden.websocket, forbidden.dom, forbidden.browser_storage, forbidden.workers, forbidden.crypto_mining (errors); forbidden.navigator, forbidden.location, forbidden.require, obfuscation.* (warnings) |
surfaces |
Renders every declared surface with sample data | surfaces.not_rendered, surfaces.load_error, surfaces.timeout, surfaces.unknown_method, surfaces.limit_exceeded, surfaces.app_error (errors; surfaces.runner_failed is the marketplace's own failure: publish again); surfaces.empty, surfaces.root_not_screen, surfaces.not_settled (warnings) |
interaction |
Presses the buttons and menus of each rendered surface | interaction.app_error (error), interaction.summary (info), interaction.developer_tests_unavailable (info; interaction.developer_tests_reported once a test report is attached) |
accessibility |
Labels and keys in the rendered trees (guideline 4.x) | a11y.button_label_missing, a11y.field_label_missing, a11y.action_label_missing, a11y.menu_item_label_missing (errors); a11y.image_alt_missing, a11y.menu_label_missing, a11y.list_key_missing, a11y.duplicate_key (warnings) |
licenses |
License files and notices in the bundle (guideline 6.x) | licenses.found (info), licenses.notices_missing (info), licenses.copyleft (warning) |
validate --strict catches most of these before you submit. interaction.developer_tests_unavailable is always informational: a package does not contain your source, so your own olan-app test suites cannot run on the marketplace; attach a signed test report instead (above). The checks may finish before the report arrives; once it is attached the note reads interaction.developer_tests_reported with the report's counts. What each rule means and how to fix it: review-guidelines.md. olan-app build keeps the license comments of bundled code (React, MIT) at the end of dist/app.js and dist/server.js, which satisfies licenses.notices_missing; extra notices go in a NOTICE or THIRD_PARTY_NOTICES file under assets/ (only assets/** is packed).
Marketplace error codes (marketplace.<code>) are listed in errors.md#olan-marketplace.