Metadata-Version: 2.5
Name: ershu-api
Version: 1.0.0.post20260812
Summary: HTTP API service for Ershu.
Project-URL: Documentation, https://ershu.org
Project-URL: Repository, https://github.com/ershu-lab/ershu-api
Project-URL: Issues, https://github.com/ershu-lab/ershu-api/issues
Author: Ershu contributors
License-Expression: Apache-2.0
License-File: LICENSE
Keywords: code-intelligence,developer-tools,ershu,fastapi
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Web Environment
Classifier: Framework :: FastAPI
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.14
Requires-Python: >=3.14
Requires-Dist: croniter<7.0.0,>=6.0.0
Requires-Dist: email-validator<3.0.0,>=2.2.0
Requires-Dist: ershu-core<2.0.0,>=1.0.1.post20260812
Requires-Dist: fastapi<1.0.0,>=0.115.0
Requires-Dist: opaque-ke-py<0.2.0,>=0.1.2
Requires-Dist: pydantic<3.0.0,>=2.10.0
Requires-Dist: pymilvus<4.0.0,>=3.0.0
Requires-Dist: redis<9.0.0,>=5.0.0
Requires-Dist: sqlalchemy[asyncio]<3.0.0,>=2.0.36
Requires-Dist: starlette<1.0.0,>=0.37.2
Requires-Dist: uvicorn[standard]<1.0.0,>=0.34.0
Provides-Extra: dev
Requires-Dist: aiosqlite>=0.22.0; extra == 'dev'
Requires-Dist: httpx>=0.28.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.25.0; extra == 'dev'
Requires-Dist: pytest>=8.3.0; extra == 'dev'
Requires-Dist: ruff>=0.15.0; extra == 'dev'
Description-Content-Type: text/markdown

# Ershu API

[简体中文](README-CN.md)

Ershu API is the HTTP backend for the Ershu web application and other HTTP clients. It is intended for people who operate Ershu and contributors working on its web boundary. The service handles account setup and login, project access, analysis run control, documents, search, chat, administration, browser sessions, and streamed responses.

Repository analysis runs in Ershu Worker. This service queues and controls that work, then serves the stored results. It does not import Worker or MCP code.

## What the API owns

| Area | Responsibility |
| --- | --- |
| Authentication | Initial administrator setup, OPAQUE login, access and refresh tokens, session cookies, logout, and login rate limiting |
| Browser security | CORS, origin checks, CSRF protection, production HTTPS checks, and proxy header handling |
| Projects | Projects, repositories, members, groups, directory groups, grants, repository credentials, and access rules |
| Analysis runs | Create, list, cancel, retry, resume, and follow up on runs; read snapshots, usage, final results, and event streams |
| Documents and context | Document views, imports and exports, project context, code context, graph queries, and search |
| Chat | Private project chat sessions, streamed answers, and source citations |
| Administration | Users, audit logs, model channels, dependency sources, vault credentials, system settings, and maintenance operations |
| Repository triggers | Signed webhooks and a separate scheduled polling process |

The API can rebuild search entries from data already stored in Ershu and expose maintenance operations. Ershu Worker owns repository analysis and the generation of project documents, code facts, and knowledge relationships.

## Where it fits

```text
Frontend and HTTP clients
           |
           v
       Ershu API --------------> PostgreSQL
           |                     Redis
           |                     Milvus
           |
           +---- queued runs ---> Ershu Worker

Ershu MCP uses shared Core services and storage. MCP traffic does not pass
through Ershu API.
```

| Component | Relationship to this repository |
| --- | --- |
| `ershu-core` | Required Python package for shared data models, permissions, queries, queue state, and database migrations |
| `ershu-worker` | Consumes queued runs and performs repository analysis and generation work |
| `ershu-mcp` | Publishes project context tools through shared Core services; it is not an API route |
| `frontend` | Uses the public HTTP, cookie, CSRF, and streaming contracts from this service |
| `ershu` | Deployment repository that pins compatible component versions and runs the complete system |

Each component is released independently. The deployment repository selects the exact set of component versions used together.

## Install and commands

| Item | Value |
| --- | --- |
| Python distribution | `ershu-api` |
| Python requirement | Python 3.14 or newer |
| HTTP service | `ershu-api` |
| Scheduled repository poller | `ershu-api-poller` |
| Database migration command | `ershu-db`, provided by `ershu-core` |
| API prefix | `/api/v1` |

Install the API as part of a tested component set through the
[`ershu` installer package](https://github.com/ershu-lab/ershu):

```bash
pip install "ershu[api]"
```

Projects that depend on the API package directly can pin the component with UV:

```bash
uv add "ershu-api==1.0.0.post20260812"
```

## Requirements

Development requires:

- Python 3.14 or newer
- [UV](https://docs.astral.sh/uv/)
- A compatible `ershu-core` release

Running the service also requires PostgreSQL, Redis, and Milvus. PostgreSQL stores application state, Redis backs login rate limiting and the poller lease, and Milvus holds vectors used by semantic search.

## Run from source

Install the locked runtime dependencies:

```bash
uv sync --locked
```

Copy `.env.example` to `.env`. Generate three independent values for `SECRET_KEY`, `ENCRYPTION_KEY`, and `MCP_KEY_PEPPER`:

```bash
uv run python -c "import secrets; print(*(secrets.token_urlsafe(48) for _ in range(3)), sep='\n')"
```

Use one output line for each setting. Do not reuse a value. The example placeholders are rejected at startup, and `.env` must not be committed.

Start PostgreSQL, Redis, and Milvus, then apply the database migrations:

```bash
uv run ershu-db upgrade head
```

Start the API:

```bash
uv run ershu-api
```

The development server listens on `127.0.0.1:8000` by default and reloads when source files change. OpenAPI pages are available only in development:

- `http://127.0.0.1:8000/docs`
- `http://127.0.0.1:8000/redoc`

## Health checks

The root and versioned health endpoints report whether the HTTP process can respond:

```bash
curl -fsS http://127.0.0.1:8000/health
curl -fsS http://127.0.0.1:8000/api/v1/health
```

Both return `{"status":"ok"}`. Check PostgreSQL and Milvus separately:

```bash
curl -fsS http://127.0.0.1:8000/api/v1/health/dependencies
```

The dependency response is `degraded` when either check fails. It does not test Redis.

## Configuration

The service reads `.env` from its working directory. `.env.example` lists the supported deployment settings.

| Group | Settings | Purpose |
| --- | --- | --- |
| Runtime | `ENVIRONMENT`, `LOG_LEVEL`, `HOST`, `PORT` | Runtime mode, logging, and listener address |
| Data services | `DATABASE_URL`, database pool settings, `REDIS_URL`, `MILVUS_URI`, `MILVUS_TOKEN`, `MILVUS_DB_NAME` | Application state, rate limiting, polling lease, and vector search |
| Service secrets | `SECRET_KEY`, `ENCRYPTION_KEY`, `MCP_KEY_PEPPER` | Sessions, encrypted stored credentials, and MCP key hashing |
| Browser sessions | token lifetimes, cookie names, `SESSION_COOKIE_SECURE`, `SESSION_COOKIE_SAMESITE`, `CORS_ORIGINS` | Login state and browser request policy |
| Reverse proxy | `PROXY_HEADERS`, `FORWARDED_ALLOW_IPS` | Forwarded scheme and client information from trusted proxies |
| Repository access | repository cache path, trusted local paths, Git host and protocol controls, provider tokens | Repository checkout and context access |
| Scheduled polling | `POLLER_INTERVAL_SECONDS`, `POLLER_LEASE_SECONDS`, `POLLER_BATCH_SIZE` | Poll frequency, ownership lease, and batch size |
| Maintenance and search | run lease, retention count, and search batch size | Queue recovery, retained history, and index maintenance |

LLM, embedding, and reranker channels are managed through the administration endpoints. Their API keys are encrypted before storage and do not belong in `.env`.

Some non-secret operational settings can be overridden in the database through the system settings endpoints. Environment values remain the startup defaults.

### OPAQUE server setup

Development can leave `OPAQUE_SERVER_SETUP` empty. On first startup, the API creates an OPAQUE server setup and stores it encrypted in PostgreSQL.

Production requires an explicit, stable value. Generate it once:

```bash
uv run python -c "import base64, opaque_ke_py; print(base64.urlsafe_b64encode(opaque_ke_py.server_setup().to_bytes()).decode('ascii').rstrip('='))"
```

Store the output in the deployment secret store. Changing this value prevents existing OPAQUE password records from being used.

## Repository polling

Repositories with polling enabled need a separate long-running process:

```bash
uv run ershu-api-poller
```

Cron expressions use UTC. Poller instances coordinate through a renewable Redis lease, so more than one instance can run without processing the same schedule at once. A single cycle can be run manually:

```bash
uv run ershu-api-poller --once
```

The API and poller must use the same PostgreSQL database, Redis instance, and encryption settings. The poller detects revisions and queues refresh runs. A running Worker is still required to process them.

## Repository webhooks

Repository webhooks use HMAC-SHA256 over this byte sequence:

```text
timestamp + NUL + nonce + NUL + raw request body
```

Send the lowercase hexadecimal digest in `X-Ershu-Webhook-Signature`, the Unix timestamp in `X-Ershu-Webhook-Timestamp`, and a unique nonce in `X-Ershu-Webhook-Nonce`. The timestamp must be within five minutes of the server clock. The exact raw body used to compute the signature must be sent unchanged.

The webhook endpoint is under `/api/v1/webhooks/repositories/{repository_id}`. A valid webhook queues work; it does not run repository analysis inside the request.

## Production checklist

- Set `ENVIRONMENT=production` and `SESSION_COOKIE_SECURE=true`.
- Keep `SECRET_KEY`, `ENCRYPTION_KEY`, `MCP_KEY_PEPPER`, and `OPAQUE_SERVER_SETUP` in a secret store. Use different, stable values.
- Terminate TLS at the service or a trusted reverse proxy. Production API requests over plain HTTP are rejected.
- Limit `FORWARDED_ALLOW_IPS` to the actual proxy addresses and pass the external host and scheme correctly.
- Add only the real frontend origins to `CORS_ORIGINS`.
- Keep `ALLOW_DEV_OPEN_LOCAL_REPOSITORY_PATHS=false`.
- Route `/api/v1/` to this service. Route `/mcp/` to Ershu MCP, not to Ershu API.

The API rejects weak or reused service secrets at startup. The development OpenAPI pages are disabled in production.

## Development

Install the development dependencies:

```bash
uv sync --extra dev --locked
```

Run the repository checks:

```bash
uv run --extra dev pytest -q
uv run --extra dev ruff check src tests
uv run --extra dev ruff format --check src tests
uv build --wheel --out-dir dist
```

The test suite uses temporary SQLite databases and replaces external services,
so it does not require live PostgreSQL, Redis, or Milvus instances.

## Repository layout

| Path | Purpose |
| --- | --- |
| `src/ershu/api_main.py` | FastAPI application, middleware, lifecycle, and HTTP runner |
| `src/ershu/api/router.py` | Route registration under `/api/v1` |
| `src/ershu/api/routes/` | HTTP routes grouped by domain |
| `src/ershu/api/schemas/` | API request and response models |
| `src/ershu/api/services/` | API-specific orchestration |
| `src/ershu/api/runtime_settings.py` | API settings and runtime validation |
| `src/ershu/api/poller.py` | Scheduled repository polling process |
| `tests/` | Route, permission, security, streaming, and maintenance tests |

Changes to HTTP requests, responses, errors, cookies, CSRF behavior, or stream
events can affect the frontend and other clients. Changes to shared data,
permissions, or query behavior belong in `ershu-core`; analysis and generation
changes belong in `ershu-worker`; MCP tool changes belong in `ershu-mcp`.

## License

Ershu API is licensed under the [Apache License 2.0](LICENSE).
