Aegra
Aegra Skill
Product summary
Aegra is an open-source, self-hosted Agent Protocol server for running LangGraph agents on your own infrastructure. It's a drop-in replacement for LangSmith Deployments—same SDK, same APIs, no vendor lock-in. Agents use Aegra to deploy production-ready servers with full persistence, streaming, authentication, and observability. Key files: aegra.json (configuration), .env (environment variables), Dockerfile and docker-compose.yml (deployment). Primary CLI commands: aegra init, aegra dev, aegra up, aegra serve. Primary docs: https://docs.aegra.dev
When to use
Reach for this skill when:
- Deploying agents: Setting up a production Aegra server for LangGraph agents
- Configuring infrastructure: Defining graphs, authentication, routes, and storage in
aegra.json - Managing state and persistence: Creating threads, running conversations, inspecting checkpoints
- Adding authentication: Implementing JWT, OAuth, Firebase, or custom auth handlers
- Setting up observability: Configuring OpenTelemetry tracing to Langfuse, Phoenix, or other backends
- Building multi-turn agents: Creating assistants, managing threads, streaming responses
- Migrating from LangSmith: Converting dashboard-based configuration to code-based setup
- Extending the API: Adding custom FastAPI routes or shared dependencies
- Debugging agent execution: Inspecting thread state, checkpoints, and run history
Quick reference
CLI commands
| Command | Use case | Starts PostgreSQL? | Starts app? |
|---|---|---|---|
aegra init |
Create new project | — | — |
aegra dev |
Local development with hot reload | Yes (Docker) | Yes (host) |
aegra up |
Self-hosted Docker production | Yes (Docker) | Yes (Docker) |
aegra serve |
PaaS, containers, bare metal (bring your own DB) | No | Yes (host) |
aegra down |
Stop containers | — | — |
Configuration files
| File | Purpose |
|---|---|
aegra.json |
Define graphs, auth, HTTP routes, store, dependencies |
.env |
Database credentials, API keys, observability settings |
Dockerfile |
Container image for production deployment |
docker-compose.yml |
PostgreSQL + app orchestration |
Key aegra.json sections
{
"graphs": [{"path": "./agent.py:graph"}],
"auth": {"path": "./my_auth.py:auth"},
"http": {"app": "./custom_routes.py:app", "cors": {...}},
"dependencies": ["./shared", "./libs"],
"store": {"index": {"dims": 1536, "embed": "openai:text-embedding-3-small"}}
}
Environment variables (essential)
| Variable | Purpose | Example |
|---|---|---|
DATABASE_URL |
PostgreSQL connection (takes precedence) | postgresql://user:pass@localhost/db |
POSTGRES_HOST, POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB |
Individual DB config (used if DATABASE_URL not set) | — |
OTEL_TARGETS |
Observability backends | LANGFUSE,PHOENIX |
LANGFUSE_PUBLIC_KEY, LANGFUSE_SECRET_KEY |
Langfuse credentials | — |
PHOENIX_COLLECTOR_ENDPOINT |
Phoenix OTLP endpoint | http://127.0.0.1:6006/v1/traces |
SDK essentials (LangGraph SDK)
from langgraph_sdk import get_client
client = get_client(url="http://localhost:2026")
# Create thread
thread = await client.threads.create()
# Create assistant
assistant = await client.assistants.create(
graph_id="agent",
name="My Agent",
metadata={"purpose": "search"}
)
# Stream run
async for chunk in client.runs.stream(
thread_id=thread["thread_id"],
assistant_id=assistant["assistant_id"],
input={"messages": [{"type": "human", "content": "Hello"}]}
):
print(chunk)
Decision guidance
When to use each deployment command
| Scenario | Command | Why |
|---|---|---|
| Local development with hot reload | aegra dev |
Starts PostgreSQL in Docker, runs app on host with auto-reload |
| Self-hosted production (your infrastructure) | aegra up |
Everything in Docker, auto-migrations, health checks, restart policies |
| PaaS (Railway, Render, Heroku) | aegra serve |
No Docker needed; you provide DATABASE_URL, app runs as process |
| Kubernetes | aegra serve |
Run in pod spec; external PostgreSQL; stateless app |
| Windows production | Docker or Linux server | aegra serve on Windows not supported (psycopg event loop issue) |
When to use authentication vs. no auth
| Situation | Approach |
|---|---|
| Local development only | Omit auth from aegra.json; no auth required |
| Production with user identity | Implement Auth handler; verify JWT, OAuth, or Firebase tokens |
| Fine-grained access control | Use permissions field in auth response; check in custom routes |
| Studio access during dev | Set AEGRA_STUDIO_AUTH_BYPASS=true to skip auth for /studio |
When to use store vs. thread state
| Use case | Approach |
|---|---|
| Conversation history, messages | Store in thread state (automatic via LangGraph) |
| User preferences, knowledge base | Use Store API (key-value or semantic) |
| Semantic search (embeddings) | Configure store.index with embedding model |
| Simple key-value (no search) | Use Store API without index config |
When to stream vs. wait
| Scenario | Approach |
|---|---|
| Real-time UI updates, token-by-token output | Use client.runs.stream() with SSE |
| Batch processing, fire-and-forget | Use client.runs.create() (background run) |
| Need final output only | Use client.runs.wait() (blocks until complete) |
| Reconnect after disconnect | Use Last-Event-ID header in stream request |
Workflow
1. Set up a new Aegra project
pip install aegra-cli
aegra init
# Choose location, template (simple-chatbot or react-agent), project name
cd <project>
cp .env.example .env
2. Define your agent graph
Write a LangGraph graph in agent.py (or your chosen path):
from langgraph.graph import StateGraph
from langgraph.types import BaseMessage
def agent_node(state):
# Your agent logic
return {"messages": [...]}
graph = StateGraph(...)
graph.add_node("agent", agent_node)
# ... add edges, compile
3. Register the graph in aegra.json
{
"graphs": [{"path": "./agent.py:graph"}]
}
4. Add authentication (if needed)
Create my_auth.py:
from langgraph_sdk import Auth
auth = Auth()
@auth.authenticate
async def authenticate(headers: dict) -> dict:
token = headers.get("Authorization", "").replace("Bearer ", "")
if not token:
raise Exception("Auth required")
# Verify JWT, OAuth, etc.
return {
"identity": "user123",
"display_name": "Jane",
"is_authenticated": True
}
Add to aegra.json:
{
"auth": {"path": "./my_auth.py:auth"}
}
5. Configure observability (optional)
In .env:
OTEL_TARGETS="LANGFUSE"
LANGFUSE_PUBLIC_KEY=pk_...
LANGFUSE_SECRET_KEY=sk_...
6. Start the server
Local development:
aegra dev
Production (Docker):
aegra up
PaaS (provide your own DB):
aegra serve
7. Create an assistant and run a conversation
import asyncio
from langgraph_sdk import get_client
async def main():
client = get_client(url="http://localhost:2026")
# Create thread
thread = await client.threads.create()
# Create assistant (or use default with graph_id)
assistant = await client.assistants.create(
graph_id="agent",
name="My Agent"
)
# Stream conversation
async for chunk in client.runs.stream(
thread_id=thread["thread_id"],
assistant_id=assistant["assistant_id"],
input={"messages": [{"type": "human", "content": "Hello"}]}
):
print(chunk)
asyncio.run(main())
8. Inspect and debug
# Get thread state
state = await client.threads.get_state(thread_id)
# Get state at specific checkpoint
state = await client.threads.get_state(
thread_id,
checkpoint={"id": "checkpoint_id"}
)
# List all assistants
assistants = await client.assistants.search()
# Get subgraphs
subgraphs = await client.assistants.get_subgraphs(assistant_id, recurse=True)
Common gotchas
-
PostgreSQL not reachable: If using
aegra devoraegra up, ensure Docker is running. If usingaegra serve, verifyDATABASE_URLorPOSTGRES_*variables in.env. -
Wrong database credentials: Check that
POSTGRES_USERandPOSTGRES_PASSWORDin.envmatch what PostgreSQL was initialized with. Migrations fail silently if connection is wrong. -
Migrations haven't applied: Server couldn't connect to PostgreSQL during startup. Check logs, fix connection, restart. Don't ignore migration errors.
-
Using
aegrapackage instead ofaegra-cli: Theaegrameta-package on PyPI doesn't support version pinning. Always installaegra-clidirectly. -
Config file resolution order: Aegra looks for
aegra.jsonfirst, thenlanggraph.json. UseAEGRA_CONFIGenv var to override. Relative paths are resolved from the config file's directory. -
Auth handler not called: Ensure
authis registered inaegra.jsonand the path is correct. Ifauthis omitted, no authentication is required. -
Embedding dimensions mismatch: The
dimsvalue instore.indexmust match your embedding model's output exactly.text-embedding-3-smalloutputs 1536 dimensions. -
Semantic store not working: Verify PostgreSQL has
pgvectorextension installed. Usepgvector/pgvector:pg18image. Check thatembedformat isprovider:model-idand API key is set. -
Custom routes not mounted: Verify
http.apppath inaegra.jsonis correct and the FastAPI app is exported. Ifenable_custom_route_authis true, auth handler must be configured. -
Windows production deployment:
aegra serveon Windows is not supported due to psycopg event loop requirements. Use Docker or deploy to Linux. -
Interrupts not working: Ensure graph calls
interrupt()fromlanggraph.types. Interrupts work across subgraph boundaries transparently. -
Streaming reconnection fails: Use
Last-Event-IDheader in stream request to resume from a specific event after disconnect. -
Health checks failing: If server hangs, Docker marks it unhealthy after 3 consecutive failures (every 30 seconds). Check logs for deadlocks or blocking operations.
Verification checklist
Before deploying or submitting work:
-
aegra.jsonis valid JSON and all paths are correct (relative to config file directory) - All graph paths in
graphssection exist and export a compiled graph - Auth handler (if configured) is importable and returns required fields (
identity,is_authenticated) -
.envfile has all required variables (at minimum:DATABASE_URLorPOSTGRES_*variables) - PostgreSQL is reachable and has
pgvectorextension (if using semantic store) - Embedding model dimensions match
store.index.dims(if configured) - Custom routes app (if configured) is a valid FastAPI instance
- Dependencies paths exist and are importable (if configured)
- CORS origins are correct for your frontend (if using custom routes)
- Observability backend credentials are set (if configured)
- Server starts without errors:
aegra devoraegra serve - Health endpoint responds:
curl http://localhost:2026/health - Can create a thread and run a conversation via SDK
- Thread state persists across runs (check with
client.threads.get_state()) - Streaming works:
async for chunk in client.runs.stream(...) - Traces appear in observability backend (if configured)
Resources
Comprehensive navigation: https://docs.aegra.dev/llms.txt
Critical documentation pages:
- Introduction & Overview — What Aegra is and key features
- Configuration Reference — Complete
aegra.jsonschema - Deployment Guide —
aegra dev,aegra up,aegra serveexplained
For additional documentation and navigation, see: https://docs.aegra.dev/llms.txt