> ## 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.

# One repo, many packages

> Keep several integrations and experiences in one repo, run them together in one harness, and publish any of them.

A **repo** is one git repository that holds many Ekanos packages: integrations and experiences side by side. You run them all in one harness, an experience uses an integration from the same repo with no setup, and you publish one package, several, or all of them.

> [!NOTE]
>
> Ekanos calls this a **repo**, not a workspace. In the Ekanos app a Workspace
> is a sidebar section, which is a different thing.

## Create a repo

```bash theme={null}
ekanos init --kind repo --slug acme-platform
cd acme-platform
pnpm install
```

`--slug` names the repo and creates `./acme-platform`; `--dir .` scaffolds in the current directory instead. One of the two is required. If the target is already inside an Ekanos repo, `init` refuses and points you at `ekanos new`.

It writes:

| File | What it is |
| - | - |
| `ekanos.json` | `{ "kind": "repo", "packages": ["integrations/*", "experiences/*"] }` |
| `package.json` | Private, with a `dev` script (`ekanos dev --start`) and the harness's dependencies |
| `pnpm-workspace.yaml` | Lists the same package globs, plus the build-script approval for `esbuild` |
| `.gitignore` | Ignores `.ekanos/` and `ekanos.local.json` |
| `integrations/`, `experiences/` | Empty folders (`.gitkeep`) |

## The root ekanos.json

```json theme={null}
{
  "kind": "repo",
  "packages": ["integrations/*", "experiences/*"]
}
```

`packages` is a list of globs, relative to the repo root. Each directory a glob matches is a package with its own `ekanos.json`, whose `kind` is `integration` or `experience`. The root file takes no other fields.

## Add packages

From the repo root or from anywhere inside it:

```bash theme={null}
ekanos new integration acme-cms
ekanos new integration acme-news
ekanos new experience content-desk
```

`ekanos new integration <slug>` scaffolds the same project as `ekanos init`, into `integrations/<slug>`. `ekanos new experience <slug>` scaffolds an [experience](https://sdk.ekanos.dev/guides/experiences.md) into `experiences/<slug>`. The slug is kebab-case and unique across the repo; a package is addressed by its slug. Outside a repo, `new` refuses and tells you to run `init --kind repo`. If a package directory is not matched by any glob in the root `packages`, it tells you to add one.

The result looks like this:

```
acme-platform/
  ekanos.json                 # kind: repo
  package.json
  pnpm-workspace.yaml
  integrations/
    acme-cms/                 # ekanos.json, src/integration.ts, ...
    acme-news/
  experiences/
    content-desk/             # ekanos.json, src/experience.ts, contracts/
```

## Run everything together

At the repo root:

```bash theme={null}
ekanos dev --start
```

One harness serves the whole repo, on port 3100 by default. You get a home page for the repo, a console for each integration at `/integrations/<name>`, and a view for each experience at `/experiences/<name>`. See [Repo mode](https://sdk.ekanos.dev/harness/repo-mode.md).

Run `ekanos dev` inside a single package instead, and the harness serves just that package.

Every other command works the same inside a package directory: `ekanos validate`, `ekanos test`, `ekanos contract`, `ekanos check`, `ekanos build`. The CLI finds the repo by walking up from the current directory to the nearest `ekanos.json` with `"kind": "repo"`.

## How an experience finds its integrations

When an experience lists `acme-cms` in its `ekanos.json` `integrations`, the harness resolves it in this order:

1. **A local override** in the experience's `ekanos.local.json` (or `EKANOS_INTEGRATION_OVERRIDE_<NAME>`), for an integration kept in a folder outside the repo.
2. **An integration package of the same repo** with that slug. This needs no setup, runs in fixtures or live, and is shown as "Local · this repo". It is **not an override**: it never blocks publishing.
3. **The registry**: an integration published to Ekanos that your Source can use, resolved from the session's catalog, the published package, or a contracts folder, and finally the committed snapshot in the experience's `contracts/` folder.

Whichever it is, the experience is checked against the **pinned contract**. The pin has to name a compat line the integration provides, and every operation you list has to exist in it. The check does not care where the code runs from.

### Mixing local and published integrations

An experience can use some integrations from the repo and others from the registry. Say `content-desk` uses `acme-cms` (a package in this repo) and `acme-social` (published by another team):

```json theme={null}
{
  "kind": "experience",
  "slug": "content-desk",
  "entry": "src/experience.ts",
  "contractsDir": "contracts",
  "integrations": [
    { "name": "acme-cms", "version": "0.2", "operations": ["tool/list_posts"] },
    {
      "name": "acme-social",
      "version": "1.0",
      "operations": ["tool/list_drafts"]
    }
  ]
}
```

`acme-cms` resolves to `integrations/acme-cms`. `acme-social` resolves to its published contract and answers from fixtures until you connect it. Give `acme-cms` a contract first (see [Operation contracts](https://sdk.ekanos.dev/guides/operation-contracts.md#derive-the-contract)), then pin both:

```bash theme={null}
cd integrations/acme-cms && ekanos contract && cd ../..
cd experiences/content-desk
ekanos add acme-cms@0.2 --ops tool/list_posts
ekanos add acme-social@1.0
```

### Pointing at a folder outside the repo

To run an experience against an integration you keep elsewhere, create `ekanos.local.json` next to the experience's `ekanos.json`:

```json theme={null}
{
  "integrations": {
    "acme-social": { "path": "../../acme-social", "mode": "live" }
  }
}
```

`path` is a folder holding an integration `ekanos.json`; `mode` is `"fixtures"` (the default) or `"live"`. The file is per-developer: `.gitignore` it, because `ekanos check` fails if it is tracked. An override like this is what a host would not resolve, so `ekanos publish` refuses an experience while one is active. Delete the file, or unset the environment variable, to publish.

## Credentials stay with the integration

An experience never holds credentials. When you connect an integration in the harness, its values are written to that integration package's own `.ekanos/local-secrets.json`, which is gitignored and never packed. See [Repo mode](https://sdk.ekanos.dev/harness/repo-mode.md#connecting-integrations).

## Publish from a repo

`ekanos publish <name>`, `ekanos publish a b`, and `ekanos publish --all` plan the packages in dependency order. See [Publishing from a repo](https://sdk.ekanos.dev/cli/publishing-a-repo.md).

## Next steps

* [Build an experience](https://sdk.ekanos.dev/guides/experiences.md)
* [Operation contracts](https://sdk.ekanos.dev/guides/operation-contracts.md)
* [Publishing from a repo](https://sdk.ekanos.dev/cli/publishing-a-repo.md)


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