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

# Architecture for Solution Developers

> The integration surface a solution sees — what the platform provides, what your solution owns, and the contracts between them.

# Architecture for Solution Developers

This page explains the platform from the solution developer's perspective: what you can rely on, what you own, and what contracts cross the boundary. It is *not* a complete platform architecture deep-dive — for that, see [Architecture Overview](/getting-started/architecture).

## Three layers, your job is the top one

CRAFT has three architectural layers. Solutions live at the top:

| Layer              | What lives here                                                                                                                                | Who owns it      |
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------- | ---------------- |
| **Solutions**      | Domain apps (Data Insights, Data Governance, your new solution)                                                                                | Solution teams   |
| **Platform**       | Identity (Keycloak), authorization (OpenFGA), Governance/Assets/Utils services, secrets, storage abstraction, LLM gateway, microfrontend shell | Platform team    |
| **Infrastructure** | Cloud provisioning, GitOps, networking, cluster lifecycle                                                                                      | SRE / Infra team |

Your solution **inherits** platform capabilities — never reimplements them. You do not run your own Keycloak. You do not write your own OpenFGA schema. You delegate via SDK calls and let the platform do the work.

## What the platform provides

```mermaid theme={null}
%%{init: {'theme': 'base', 'themeVariables': {'lineColor': '#555555', 'fontFamily': 'sans-serif', 'edgeLabelBackground': '#ffffff'}}}%%
flowchart TB
    subgraph Solution["Your solution (em-&lt;solution&gt; namespace)"]
        UI["UI<br/>(microfrontend)"]
        API["API service<br/>(FastAPI)"]
        WORKER["Worker / agent<br/>(optional)"]
        DB[("Your DB<br/>(Postgres)")]
        UI --> API
        API --> WORKER
        API --> DB
    end

    subgraph Platform["Platform (em-runtime namespace)"]
        KC["Keycloak<br/>(OIDC/JWT)"]
        GOV["Governance<br/>(8000)"]
        ASSETS["Assets<br/>(8000)"]
        UTILS["Utils<br/>(8000)"]
        SECRETS["Secrets<br/>(Infisical/ESO)"]
        OBSTORE["Object storage<br/>(obstore)"]
        LITELLM["LiteLLM gateway"]
    end

    subgraph External["External"]
        MODELS["Model providers<br/>(OpenAI, Anthropic, Gemini, ...)"]
    end

    API -->|JWT| KC
    API -->|"register agents,<br/>connections"| ASSETS
    API -->|permission checks| GOV
    API -->|secrets at runtime| SECRETS
    API -->|read/write blobs| OBSTORE
    API -->|inference| LITELLM
    LITELLM --> MODELS

    classDef solution fill:#56B4E9,stroke:#555555,color:#000
    classDef platform fill:#0072B2,stroke:#555555,color:#fff
    classDef store fill:#009E73,stroke:#555555,color:#000
    classDef external fill:#CC79A7,stroke:#555555,color:#000
    class UI,API,WORKER solution
    class KC,GOV,ASSETS,UTILS,SECRETS,OBSTORE,LITELLM platform
    class DB store
    class MODELS external
```

| Capability        | Where                                                       | What you do                                                                                                                              |
| ----------------- | ----------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| Authentication    | Keycloak (multi-realm OIDC/PKCE)                            | Validate JWTs in your service. See [Authenticate Users](/guides/solution-dev/authenticate-users).                                        |
| Authorization     | OpenFGA via Governance SDK                                  | Forward the user JWT + resource ID; receive allow/deny.                                                                                  |
| Multi-tenancy     | `org_id` (from JWT) + `X-Project-ID` header                 | Read both from request context; scope all queries by them. See [Concepts](/getting-started/concepts).                                    |
| Resource registry | Assets API (port 8000)                                      | Register agents, data connections, files, models. See [Register a Solution](/guides/solution-dev/register-a-solution).                   |
| Secrets           | Infisical (self-hosted) or ESO + GCP Secret Manager (cloud) | Declare in your chart's `envVars`; read from `os.environ` or `/mnt/secrets/`. See [Manage Secrets](/guides/solution-dev/manage-secrets). |
| Shared storage    | `obstore` abstraction over S3, GCS, Azure Blob, MinIO       | Initialize a client from a Connection; `put`/`get`/`list`. See [Use Shared Storage](/guides/solution-dev/use-shared-storage).            |
| LLM access        | LiteLLM gateway (provider-agnostic)                         | Set `LLM_GATEWAY_URL`; call with the OpenAI SDK. See [Access LLMs](/guides/solution-dev/access-llms).                                    |
| Observability     | Langfuse for LLM traces; OpenTelemetry for everything else  | Set `LANGFUSE_HOST`; instrument with OTel SDK. See [Langfuse Setup](/guides/langfuse-setup).                                             |
| UI shell          | `em-runtime-ui` (React Module Federation 2.0)               | Expose your UI as a remote module. (Out of scope for v1 of this guide.)                                                                  |

## What your solution owns

You own everything that is specific to your domain:

* **The UI** (typically a React microfrontend remote module loaded into `em-runtime-ui`)
* **The backend services** (FastAPI + uvicorn is the platform default; nothing stops you choosing differently)
* **Your own PostgreSQL database** — *no cross-service foreign keys with platform DBs*. Each service owns its data; consistency across services is by API contract, not joins.
* **Your Helm chart** that wraps `em-service` (one alias per component)
* **Optional Prefect workflows** for orchestration-heavy work
* **Optional A2A or Pydantic AI agents** for agentic capabilities
* **Your CI/CD pipeline** (per-repo, calling shared modules)

## Integration contracts

Five contracts cross the platform-solution boundary. Get these right and the rest is implementation detail.

### 1. JWT (authentication)

Every request to your service carries `Authorization: Bearer <jwt>`. The token is issued by Keycloak in the user's organization realm. Validate the signature against Keycloak's JWKS, extract `org_id` from the issuer URL, and reject requests with no token. See [Authenticate Users](/guides/solution-dev/authenticate-users) for code.

### 2. `X-Project-ID` header (project scoping)

JWT alone gives you organization context. The active project is communicated via `X-Project-ID`. Every database query, storage path, and downstream call must be scoped by both. See [Concepts](/getting-started/concepts) for the full multi-tenancy model.

### 3. Connections API (data sources)

When your solution needs to talk to a customer database or object store, you do *not* ask the customer for credentials directly. They register a [Data Connection](/platform/data-connections) via Assets, and you fetch the configured connection at runtime. Credentials never live in your code or config.

### 4. obstore (shared storage)

For artifacts, files, and intermediate data, use the platform's `obstore` abstraction. Same code works against S3, GCS, Azure Blob, and local MinIO — pick the backend with the configured connection. See [Use Shared Storage](/guides/solution-dev/use-shared-storage).

### 5. LiteLLM gateway (LLM access)

Solutions never call OpenAI or Anthropic directly. Instead, point your OpenAI-compatible SDK at `LLM_GATEWAY_URL`. The gateway provides central key management, per-project cost attribution, rate-limit enforcement, and observability via Langfuse. See [Access LLMs](/guides/solution-dev/access-llms).

## What does NOT cross the boundary

| You should never…                               | Because…                                                                   |
| ----------------------------------------------- | -------------------------------------------------------------------------- |
| Run your own Keycloak                           | Multi-realm setup is platform infrastructure; rolling your own breaks SSO  |
| Write your own RBAC                             | OpenFGA is the platform's source of truth; permission decisions stay there |
| Bake credentials into images or git             | Use the secrets pipeline; rotation depends on it                           |
| Add a foreign key from your DB to a platform DB | Breaks independent restore/backup; use API contracts instead               |
| Call OpenAI/Anthropic from your service code    | LiteLLM gateway is the only attribution surface                            |
| Access another solution's database directly     | Cross-service joins are forbidden; use the other solution's API            |

## Next steps

<CardGroup cols={2}>
  <Card title="Quickstart" icon="rocket" href="/guides/solution-dev/quickstart">
    Build a working hello-solution in 30 minutes.
  </Card>

  <Card title="Register a solution" icon="clipboard-check" href="/guides/solution-dev/register-a-solution">
    Naming, namespaces, image registries, registration steps.
  </Card>

  <Card title="Local development" icon="laptop-code" href="/guides/solution-dev/local-development">
    Iterate quickly with docker-compose, hot reload, and mocked platform services.
  </Card>

  <Card title="Concepts" icon="book" href="/getting-started/concepts">
    Read the platform-side concepts (orgs, projects, X-Project-ID).
  </Card>
</CardGroup>
