> ## Documentation Index
> Fetch the complete documentation index at: https://docs.evox.wraithesports.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Publishing

> Prepare and validate your pack with the CLI and submit it to Market moderation; learn the server-side security pipeline.

This page describes the end-to-end flow of publishing your pack to the evoX Market: which pack types can be published today, how to prepare them with the CLI, and which security gates the server runs your pack through.

## What can be published today

<Columns cols={2}>
  <Card title="Data-only packs — open">
    Ready profiles, action packs, icon packs and sound packs use the real moderation flow and can be published.
  </Card>

  <Card title="Executable plugin — closed">
    Public executable plugin upload is currently in the `503 PLUGIN_UPLOAD_NOT_READY` state. For details see <a href="/en/sdk/distribution-status">Distribution status</a>.
  </Card>
</Columns>

<Info>There is **no** separate publisher application. Anyone who is signed in can upload and submit to moderation; the only gate is moderation. Only the pack owner can submit a pack to moderation. The “Verified” badge exists only on official wraithesports content.</Info>

## Prerequisites

| Requirement         | Description                                                              |
| ------------------- | ------------------------------------------------------------------------ |
| evoX account        | Must be signed in with Google/JWT (the only requirement)                 |
| Validated pack file | An `.evoxpack` file with a valid `manifest.json`                         |
| Passing scan        | The version must have `scanStatus: passed` and `scanDigest === fileHash` |

## Step 1 — Prepare the pack with the CLI

`@evoxapp/plugin-cli` (`evox-plugin`) validates the project, produces a bundle and packages an `.evoxpack` with the `evox-archive-v1` policy.

```bash theme={}
evox-plugin validate ./my-plugin
evox-plugin build ./my-plugin
evox-plugin pack ./my-plugin
evox-plugin publish-check ./my-plugin
```

<Warning>`publish-check` is a local parity check; it does not replace moderation, a production scanner, signing or revocation. Passing it does not prove the pack is public-ready.</Warning>

For the detailed command table, see the <a href="/en/sdk/cli">Developer CLI</a> page.

<Note>The CLI `pack` output is an executable pack carrying `packageType: plugin`. Because executable upload is closed, this pack gets `503 PLUGIN_UPLOAD_NOT_READY` in Step 2 today. The data-only packs that can be published today are produced by the desktop app: <a href="/en/application/publish-your-pack">Publish your own pack</a>.</Note>

## Step 2 — Upload

The validated `.evoxpack` file is uploaded as multipart to the `POST /packages/upload` endpoint. The upload is accepted from any authenticated (signed-in) session; no separate publisher approval is needed.

The server first writes the file to secure quarantine, then scans it **within the same request**. If the scan passes, the upload response returns a draft (`draft`) version; if not, an error code is returned and no draft is created. The draft is not published yet: it is submitted separately with the **Moderation submission** step below.

## Step 3 — Server security pipeline

Every uploaded pack goes through the following gates. This flow is server-authoritative; no client field can change the result.

<Steps>
  <Step title="Quarantine">
    The file is SHA-256 hashed and written to an immutable private quarantine area. If the storage write fails, the draft is **not created** (fail-closed).
  </Step>

  <Step title="Static scan">
    Archive policy (zip bomb / path traversal / size), magic-byte match, SVG active content, secret leakage, forbidden extension and legacy identifier checks are performed.
  </Step>

  <Step title="Draft record">
    If the scan passes, the pack is recorded as a draft version atomically bound to the scan evidence (`policyVersion`, `scanDigest`, `findings`).
  </Step>

  <Step title="Moderation submission">
    The pack owner submits the version to moderation with `POST /submissions`. The server verifies `scanStatus: passed` and `scanDigest === fileHash` again.
  </Step>

  <Step title="Approval">
    A moderator reviews the scan evidence and approves/rejects. Approval publishes the pack on the Market.
  </Step>
</Steps>

## Main content rejected by the scan

* **Secret leakage:** private keys, API tokens, JWTs or real credential values in config.
* **Magic-byte mismatch:** files whose extension does not match their real content type.
* **Active SVG:** SVG containing scripts, event handlers or external references.
* **Forbidden extension (data-only):** `.exe`, `.dll`, `.js`, `.bat` and similar executable/active files.
* **Legacy identifier:** use of old StreamDock UUIDs.
* **Local path leakage:** absolute file paths inside the manifest or profile.

## Upload error codes

| Code                      | Meaning                                                                    |
| ------------------------- | -------------------------------------------------------------------------- |
| `UNAUTHORIZED`            | No session or it could not be verified (401)                               |
| `PACKAGE_FILE_REQUIRED`   | The pack file is missing (400)                                             |
| `PACKAGE_STORAGE_FAILED`  | The quarantine write failed (503, fail-closed)                             |
| `PACKAGE_SCAN_FAILED`     | The pack did not pass the security policy (400)                            |
| `PACKAGE_BIND_FAILED`     | The scan result could not be bound to the draft version (503, fail-closed) |
| `PLUGIN_UPLOAD_NOT_READY` | The executable plugin pipeline is closed (503)                             |

When uploading a new version to an existing product, these codes can also be returned:

| Code                         | Meaning                                                                    |
| ---------------------------- | -------------------------------------------------------------------------- |
| `MARKET_ITEM_NOT_FOUND`      | The product to add the version to was not found (404)                      |
| `PACKAGE_OWNERSHIP_REQUIRED` | The product does not belong to you (403)                                   |
| `PACKAGE_TYPE_MISMATCH`      | The new version's pack type is not the same as the product's (409)         |
| `VERSION_NOT_SEMVER`         | The version number is not semver; it should be, for example, `1.0.1` (409) |
| `VERSION_NOT_INCREMENTED`    | The version number is not greater than the current highest version (409)   |
| `DUPLICATE_VERSION_CONTENT`  | The content is identical to an existing version (409)                      |
| `CHANGELOG_REQUIRED`         | The changelog is empty (400)                                               |

## Moderation submission error codes

| Code                          | Meaning                                                                      |
| ----------------------------- | ---------------------------------------------------------------------------- |
| `SUBMISSION_TARGET_NOT_FOUND` | The pack was not found (404)                                                 |
| `PACKAGE_OWNERSHIP_REQUIRED`  | Only the owner can submit the pack (403)                                     |
| `PACKAGE_VERSION_NOT_FOUND`   | There is no scanned draft version to submit (404)                            |
| `SUBMISSION_ALREADY_PENDING`  | A version of this pack is already in moderation (409)                        |
| `PACKAGE_FILE_REQUIRED`       | The draft version has no validated pack file (409)                           |
| `PACKAGE_SCAN_NOT_PASSED`     | A version that did not pass the scan cannot be submitted to moderation (409) |

<Note>`EXECUTABLE_PUBLISHER_APPROVAL_REQUIRED` and `PLUGIN_ID_ALREADY_CLAIMED` are not upload or submission errors; they are returned from the request to claim an executable plugin identity. Because executable upload is closed, this step does not open a publication path today.</Note>

<Warning>A static scan, a local sandbox smoke or a working T1 example is not public-ready on its own. The `503` gate cannot be bypassed with a client fallback, a metadata-only upload or a generic "passed" result.</Warning>
