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

# Quickstart

> Deploy your first AI agent with Idun in under 30 minutes, from pip install to live chat UI.

`pip install` → chat UI → production agent in under 30 minutes. No Docker required, no separate frontend, no extra services to deploy.

## Prerequisites

* **Python 3.12+** ([python.org](https://www.python.org/downloads/))
* **pip** (bundled with Python)
* An LLM provider key (OpenAI, Anthropic, or Google, whichever your agent calls)

## Steps

<Steps>
  <Step title="Create a project directory and install">
    ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
    mkdir my-agent && cd my-agent
    pip install idun-agent-engine langgraph langchain-google-genai
    ```

    Save the next two files inside this `my-agent/` directory.
  </Step>

  <Step title="Save agent.py">
    Save this as `my-agent/agent.py`:

    ```python agent.py theme={"theme":{"light":"github-light","dark":"github-dark"}}
    from typing import Annotated, TypedDict

    from langchain_google_genai import ChatGoogleGenerativeAI
    from langgraph.graph import StateGraph, START, END
    from langgraph.graph.message import add_messages


    class State(TypedDict):
        messages: Annotated[list, add_messages]


    llm = ChatGoogleGenerativeAI(model="gemini-2.0-flash")


    def chatbot(state: State):
        return {"messages": [llm.invoke(state["messages"])]}


    graph = StateGraph(State)
    graph.add_node("chatbot", chatbot)
    graph.add_edge(START, "chatbot")
    graph.add_edge("chatbot", END)
    ```
  </Step>

  <Step title="Save config.yaml">
    Save this as `my-agent/config.yaml` next to `agent.py`:

    ```yaml config.yaml theme={"theme":{"light":"github-light","dark":"github-dark"}}
    server:
      api:
        port: 8000

    agent:
      type: LANGGRAPH
      config:
        name: "my-agent"
        graph_definition: "./agent.py:graph"
        checkpointer:
          type: sqlite
          db_url: "sqlite:///conversations.db"
    ```

    <Note>
      `graph_definition: "./agent.py:graph"` is resolved against the directory where you run `idun init` (the next step), so `agent.py` and `config.yaml` must live in the same directory you launch from. Use an absolute path if you want to invoke it from elsewhere.
    </Note>
  </Step>

  <Step title="Run">
    Put `GEMINI_API_KEY=...` in a `my-agent/.env`. Get a key at [aistudio.google.com/apikey](https://aistudio.google.com/apikey).

    From inside `my-agent/`:

    ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
    idun init
    ```

    Runs Alembic migrations, seeds the database from `config.yaml`, and boots the server at `http://localhost:8000`. Use `idun init --port 8080` to bind a different port (overrides the `IDUN_PORT` env var; default is 8000).

    <Warning>
      `config.yaml` is a **one-shot bootstrap**, not a runtime config file. Each resource (agent, MCP servers, guardrails, observability, …) is seeded into the DB only if the corresponding row is empty. After the first boot the admin panel at `/admin/` becomes the source of truth, and later edits to `config.yaml` are ignored. To re-seed from YAML, clear the relevant rows from the DB (or delete `idun_standalone.db` for a clean restart) and run `idun init` again.
    </Warning>

    <Frame>
      <img alt="Admin landing page" src="https://mintcdn.com/idunlabs/SjVPzIbyPaldjUKK/images/readme/dashboard.png?fit=max&auto=format&n=SjVPzIbyPaldjUKK&q=85&s=94b9f8b306a60a0d99fb1c91ec991049" width="1440" height="900" data-path="images/readme/dashboard.png" />
    </Frame>
  </Step>

  <Step title="Chat">
    Open [http://localhost:8000](http://localhost:8000) in your browser. Send a message and the response streams back in real time.

    <Frame>
      <img alt="Chat conversation" src="https://mintcdn.com/idunlabs/SjVPzIbyPaldjUKK/images/readme/chat.png?fit=max&auto=format&n=SjVPzIbyPaldjUKK&q=85&s=68fbb955104ab0184c9f6609d4b45756" width="1440" height="900" data-path="images/readme/chat.png" />
    </Frame>
  </Step>

  <Step title="Explore the admin">
    Open [http://localhost:8000/admin](http://localhost:8000/admin). Configure MCP servers, managed prompts, observability, messaging integrations, and SSO, all without redeploying.

    <Frame>
      <img alt="Agent detail" src="https://mintcdn.com/idunlabs/SjVPzIbyPaldjUKK/images/readme/agent-detail.png?fit=max&auto=format&n=SjVPzIbyPaldjUKK&q=85&s=2baf821a71dc207afa3d49279835ce72" width="1440" height="900" data-path="images/readme/agent-detail.png" />
    </Frame>
  </Step>
</Steps>

## Next steps

<Card title="Pick a framework" icon="layers" horizontal href="/frameworks/overview">
  LangGraph or Google ADK
</Card>

<Card title="Add guardrails" icon="shield" horizontal href="/guardrails/overview">
  15+ built-in safety guards
</Card>

<Card title="Wire observability" icon="chart-line" horizontal href="/observability/overview">
  Langfuse, Phoenix, LangSmith, or GCP Trace
</Card>

<Card title="Connect MCP servers" icon="plug" horizontal href="/mcp-servers/overview">
  stdio, SSE, streamable HTTP, or WebSocket transports
</Card>

<Card title="Deploy" icon="cloud" horizontal href="/deployment/overview">
  Cloud Run, Kubernetes, or any single-container host
</Card>

## Switching to Postgres

The standalone runtime ships with SQLite as the default database for quickstart and demo. For production, switch to Postgres. SQLite's trace-storage ceiling is around 10k traces before the list view starts to lag. The trace UI shows a permanent banner reminding operators of this when SQLite is the active backend.

To switch:

<Steps>
  <Step title="Provision Postgres 15+">
    Provision a Postgres 15 or newer database. Make sure the `pg_trgm` extension is available, used for free-text trace search.
  </Step>

  <Step title="Set DATABASE_URL">
    ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
    export DATABASE_URL="postgresql+asyncpg://user:pass@host:5432/dbname"
    ```
  </Step>

  <Step title="Run migrations">
    ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
    idun setup
    ```

    Alembic creates the trace tables with monthly partitioning, plus the standard standalone admin tables.
  </Step>

  <Step title="Restart the standalone runtime">
    ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
    idun init
    ```
  </Step>
</Steps>

Postgres unlocks 5–8k spans/sec sustained write (Linux production floor) versus 1k spans/sec on SQLite, plus richer free-text search and bounded read latency at scale.

### Trace-store environment variables

| Variable                            | Default | Purpose                                                                                                                                                               |
| ----------------------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `IDUN_TRACE_RETENTION_DAYS`         | `14`    | How many days of traces to keep before dropping. On Postgres, expired monthly partitions are detached and dropped. On SQLite, a scheduled `DELETE` runs.              |
| `IDUN_TRACES_INPUT_VALUE_MAX_BYTES` | `65536` | Per-attribute byte cap before truncation. Lower this on heavy-payload deployments to recover throughput, or raise it to keep more raw input/output.                   |
| `IDUN_PRICES_REFRESH`               | `false` | When `true`, the cost calculator fetches the LiteLLM model-prices snapshot at boot (5-second timeout, snapshot fallback). Default uses the vendored monthly snapshot. |
