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

# Repo mode

> Run every integration and experience of a repo in one harness, connect each integration once, and choose live or fixtures per integration.

Run `ekanos dev` at the root of an [Ekanos repo](https://sdk.ekanos.dev/guides/one-repo-many-packages.md) and you get **repo mode**: one harness for every package, at `http://localhost:3100` unless you pass `--port`.

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

Run it inside one package instead and the harness serves only that package, exactly as a single project.

## The repo home

`/` is "This repo". Along the top, five counts: **Integrations**, **Experiences**, **Connected**, **No connection needed** and **Need connecting**.

**Integrations** lists one card per integration package:

* its name, version and contract version;
* a connection chip (below) and, if it is not connected yet, the kind of connection it needs: API keys, OAuth or Shipped config;
* **After publishing**: the levels it can be connected at once published (Source, Team, Each person), from the integration's declared levels;
* **Local store**: where its credentials live, repo-relative;
* a health cell with **Check health** (an integration that is not connected yet says "Not checked locally"), the connection controls, and **Open console**.

**Experiences** lists one card per experience package: its name and version, the integrations it uses with each one's connection chip, a **View** button and a **Connect checklist** button.

The old `/connections` page is folded into this home. `/connections` redirects to `/`.

Each integration package keeps its [console](https://sdk.ekanos.dev/harness/integration-console.md) at `/integrations/<name>`, and each experience its view at `/experiences/<name>` with Summary, Widgets, Workflows, Review queue, Run log and Connect tabs.

## One word for connection

Every screen in the harness (the repo home, the Connect tab, the experience checklist, the activation page, the OAuth panel) states a connection the same way, from one function that reads the contract's auth, the keys the code declares, and OAuth.

| Status | Meaning |
| - | - |
| **Connected** | Credentials are stored locally. The chip also lists the non-sensitive fields they hold, such as `local credentials · dataset production`. A credential-named field is never shown, not even masked |
| **Not connected** | It needs credentials and has none |
| **No connection needed** | The contract's auth is `none` and it declares no keys: nothing to connect |
| **Managed by Ekanos** | A published integration whose code is not in your repo: Ekanos holds its connection, there is nothing to store locally, and it answers from fixtures |

Next to the status, **Live** or **Fixtures** says what the integration answers from right now.

## Connecting integrations

Connect an integration from wherever you are working. An experience's **Connect checklist** (`/experiences/<name>/connect`) has one row per integration it uses; **Connect** and **Edit** open the integration's form in a dialog (the activation form, the keys form, or the OAuth start), and **Disconnect** removes the stored values. The same controls are on the repo home and on an integration's own Connect tab.

### Where credentials are stored

Credentials are stored **per integration**, in the package's own `.ekanos/local-secrets.json`:

```
integrations/acme-cms/.ekanos/local-secrets.json
integrations/acme-news/.ekanos/local-secrets.json
```

* The file is gitignored (`.ekanos/` is), never packed by `ekanos publish`, and written with mode `0600`.
* An experience never gets a copy. It reaches the integration through its operations, and the harness runs the integration's code with the credentials in the integration's folder.
* **They last until you Disconnect.** They survive a harness restart, a browser reload, and `ekanos dev` regenerating `.ekanos/harness`. Saving again replaces the old values.
* For an integration with OAuth, the tokens go in the same file and are replayed through your `onTokens` handler when the harness starts, so you are not asked to authorize again. They are not replayed if the OAuth app's client id changed.
* **Disconnect** removes only that integration's stored activation fields and API keys. It keeps the OAuth app's own keys and the file's `0600` mode, and leaves the form ready for new values.

> [!WARNING]
>
> Treat `.ekanos/local-secrets.json` as a secret. Don't copy it into an issue or
> a chat, and never commit it.

### OAuth on localhost

For an integration that declares `oauth`, register this redirect URL with the provider's developer app:

```
http://localhost:<port>/api/integrations/<slug>/oauth/callback
```

`<port>` is the harness port (3100 by default) and `<slug>` is the integration's slug. The Local OAuth panel on the Connect tab shows the URL with a Copy button, together with the scopes your definition declares. Save the client id and secret in the panel, then click **Connect**. See [Live mode](https://sdk.ekanos.dev/harness/live-mode.md#oauth-loop-on-localhost) for the full loop.

## Live or fixtures, per integration

There is no global **Data** selector in repo mode. Each integration decides for itself.

| Integration is | Answers from |
| - | - |
| Connected | **Live**, with its stored credentials |
| Not connected | **Fixtures** |
| No connection needed | **Live** (nothing to connect) |
| Managed by Ekanos | **Fixtures** (its code is not here to run) |
| Pinned to fixtures | **Fixtures**, whatever else is true |

Fixtures are `fixtures/<integration>/<kind>-<op>.<default|empty|error>.json` for the scenario you pick, and the example in the contract when there is none. On an experience view, every integration shows a chip such as `acme-cms · live`, `acme-social · fixtures (not connected)` or `acme-news · live (no connection needed)`, with the matching controls.

**Pin to fixtures** keeps an integration on fixtures even when it is connected, so you can build against stable data without disconnecting. The chip then reads "Fixtures · pinned" with "Saved for this repo", and the button becomes **Unpin**. The pin is stored in `.ekanos/local-state.json` next to the integration's other local files:

```json theme={null}
{ "pinnedFixtures": { "acme-news": true } }
```

It is gitignored, separate from credentials, and survives a restart and the regeneration of `.ekanos/harness`. **Disconnect does not clear it.** Unpin removes it.

> [!WARNING]
>
> **Live writes are real.** On Live, a write operation approved in the approvals
> tray calls the real service: a draft is created, a post is published. Reads
> run live without asking; writes are always held for your approval first. Pin
> an integration to fixtures, or keep it unconnected, while you are building a
> workflow that writes.

## Provenance

Each integration also says where it runs from:

* **Local · this repo**: a package of this repo. It is not an override, and it never blocks publishing.
* A **local override** in the experience's `ekanos.local.json`: an integration folder outside the repo. The harness warns, because publishing is refused while one is active.
* **Registry**: a published integration, resolved from the catalog or from a contracts folder.

See [One repo, many packages](https://sdk.ekanos.dev/guides/one-repo-many-packages.md#how-an-experience-finds-its-integrations).

## Next steps

* [The integration console](https://sdk.ekanos.dev/harness/integration-console.md)
* [Build an experience](https://sdk.ekanos.dev/guides/experiences.md)
* [Live mode](https://sdk.ekanos.dev/harness/live-mode.md)


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