> ## Documentation Index
> Fetch the complete documentation index at: https://sdk.ekanos.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Publishing from a repo

> Publish one package, several, or all of them from an Ekanos repo, in dependency order, with a dry run that builds every artifact and touches no network.

In an [Ekanos repo](https://sdk.ekanos.dev/guides/one-repo-many-packages.md), `ekanos publish` plans the packages first, checks every one, and only then submits anything. Everything on [Publishing & target verification](https://sdk.ekanos.dev/cli/publishing.md) (seats, `--source`, `--yes`, exit codes) still applies to each integration it submits.

## Choose what to publish

```bash theme={null}
ekanos publish acme-cms               # one package, by slug
ekanos publish acme-cms acme-news     # several
ekanos publish --all                  # every package in the repo
ekanos publish --all --dry-run        # build everything, send nothing
```

* Name packages **or** pass `--all`, not both.
* Run from the repo root, a package name is required (or `--all`). Run from **inside** a package directory, a bare `ekanos publish` publishes that package.
* A name that is not a package of the repo is a usage error that lists the packages.
* `--all` refuses to run while any package's `ekanos.json` is unreadable, and names it.
* Package names and `--all` outside a repo are a usage error. A plain project publishes as before.

## The plan

The plan is the packages you chose, in **topological order**: an integration comes before every experience that uses it. The CLI prints it first:

```
Dry run: publishing 3 packages in this order:
  1. acme-cms (integration 0.2.0, global)
  2. acme-news (integration 0.1.4, restricted: acme-media)
  3. content-desk (experience 0.1.0, global)
```

Because an integration of the same repo is not an override, it is ordered first rather than refused. If you select an experience whose same-repo integration is **not** in the selection, the plan warns you: publish that integration first, or make sure it is already published at a compatible version. Nothing stops you; the warning is the check.

## Every package is checked before anything is sent

Each package passes its own gate:

* an integration: the same findings pass as `ekanos validate`, with the publish escalations (see [Complete publish flow](https://sdk.ekanos.dev/cli/publishing.md#complete-publish-flow));
* an experience: the experience gate, as `ekanos check`, and the refusals in [Build an experience](https://sdk.ekanos.dev/guides/experiences.md#publishing) (an active local override, a tracked `ekanos.local.json`, any `ignore` entry).

If any package fails, the CLI prints a summary table, says **"Nothing was published."**, and exits `10` (`gate_failed`). The status column says which package and why. Fix them, then re-run.

## Dry run

```bash theme={null}
ekanos publish --all --dry-run
```

`--dry-run` gates every package, **builds every artifact**, prints a summary, and touches no network. It needs no login and no `--source`.

Each package gets a folder under the repo root:

```
.ekanos/publish/<name>-<version>/
```

| Package | Contents |
| - | - |
| Integration | `<name>-<version>.tgz` (the same archive a publish would send), `manifest.json`, and `contract.json` when the package has one |
| Experience | The packed tarball, and `manifest.json` |

An experience's pack is checked: it may contain `dist/`, `manifest.json`, `ekanos.json`, `ekanos.lock.json` and `contracts/`, and nothing else. Integration code in the tarball fails the dry run.

The summary:

```
Dry run: 3 packages built under .ekanos/publish. Nothing was published.

package       kind         version  availability              artifact                              status
acme-cms      integration  0.2.0    global                    .ekanos/publish/acme-cms-0.2.0        built
acme-news     integration  0.1.4    restricted: acme-media    .ekanos/publish/acme-news-0.1.4       built
content-desk  experience   0.1.0    global                    .ekanos/publish/content-desk-0.1.0    built
```

`.ekanos/` is gitignored. With `--json`, `data` carries `repo`, `dryRun`, `order`, `warnings`, `notes`, `packages` (one row each, with `status`, `artifact`, `availability`, any `findings`) and `published: false`.

## A real publish

Without `--dry-run`, the CLI submits the **integrations** in plan order, each through the usual path (session, source resolution, seat verification, the 4 MiB archive limit, a version that has not been submitted before). The `--source`, `--host` and `--yes` flags apply to every one.

* If an integration fails to submit, the run stops there. Its row says `failed`, the ones after it say `not attempted`, and the earlier ones stay submitted.
* Each submitted row carries the host's receipt (`submissionId`, `state`, and so on) in `--json`.

> [!WARNING]
>
> **Publishing an experience is not supported by the host yet.** A real run
> submits the integrations in your selection, marks each experience `not
>     supported by the host`, and then exits `10` with "Publishing an experience is
> not supported by the host yet." The integrations were submitted. Use `ekanos
>     publish --dry-run` to build and pack an experience; that is the only thing it
> does today.

## Availability

A package can say who may use it once it is published, in its own `ekanos.json`:

```json theme={null}
{
  "kind": "integration",
  "slug": "acme-news",
  "entry": "src/integration.ts",
  "availability": { "mode": "restricted", "sources": ["acme-media"] }
}
```

**availability.mode** `'global' | 'restricted'` *required*

`global` means every Source may use the package. `restricted` means only the
Sources named in `sources`, plus the one that owns it. A Source is a tenant of
Ekanos, such as your company's deployment.

**availability.sources** `string[]`

Source slugs, kebab-case, at least one if present. Only with `restricted`;
`global` with `sources` is a validation error. A `restricted` package with no
`sources` is available to its owner only.

No `availability` field means `global`. The plan and the summary table show each package's availability as `global`, `restricted: acme-media` or `restricted: owner only`.

> [!NOTE]
>
> **Availability is declared, not enforced yet.** The host does not take
> `availability` at submission today. For each integration that declares one,
> the plan's summary ends with `note: acme-news: restricted: acme-media -
>     availability is set by a super admin today; the host does not take it at
>     submission.` A super admin sets who can use the package. Declare it now: the
> CLI shows it in the plan and the table, so it is ready when the host starts
> reading it.

## Exit codes

| Code | Meaning |
| - | - |
| `0` | Dry run built, or every selected integration submitted |
| `2` | Usage: names with `--all`, neither given from the root, or an unknown package |
| `3` | Validation: an unreadable package manifest under `--all`, or a malformed root `ekanos.json` |
| `9` | Precondition: the repo has no packages yet |
| `10` | `gate_failed`: a package failed its gate, or an experience was selected in a real run |

Authentication, seat and version-already-submitted exit codes are those of a single [publish](https://sdk.ekanos.dev/cli/commands.md#publish).

## Next steps

* [One repo, many packages](https://sdk.ekanos.dev/guides/one-repo-many-packages.md)
* [Operation contracts](https://sdk.ekanos.dev/guides/operation-contracts.md)
* [Publishing & target verification](https://sdk.ekanos.dev/cli/publishing.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.