npx skills add ...
npx skills add https://docs.aegra.dev --skill aegra
Use when deploying LangGraph agents to production, building multi-turn conversational AI systems, managing agent state and persistence, implementing human-in-the-loop workflows, or setting up self-hosted agent infrastructure with authentication and observability.
npx skills add https://docs.aegra.dev --skill aegra
Aegra is an open-source, self-hosted Agent Protocol server for running LangGraph agents on your own infrastructure. It provides production-ready features including persistent state via PostgreSQL, real-time streaming with Server-Sent Events, human-in-the-loop approval gates, configurable authentication (JWT/OAuth/Firebase), semantic search with pgvector, and deployment to Docker, PaaS, or Kubernetes. Use the LangGraph SDK you already know — Aegra is a drop-in replacement for LangSmith Deployments with zero code changes.
Key files and commands:
aegra.json — Configuration file defining graphs, auth, HTTP routes, store settings, and checkpointer behavioraegra dev — Start development server with hot reload and managed PostgreSQLaegra serve — Start production server (requires external PostgreSQL)aegra up — Start full stack in Docker (PostgreSQL + Redis + app).env — Environment variables for API keys, database, and Redis configurationPrimary docs: https://docs.aegra.dev
Reach for this skill when:
| Command | Purpose | When to use |
|---|---|---|
aegra init | Create new project | Starting a new agent project |
aegra dev | Dev server with hot reload | Local development |
aegra serve | Production server | PaaS, Docker, Kubernetes, bare metal |
aegra up | Full Docker stack | Self-hosted production with PostgreSQL + Redis |
aegra down | Stop Docker services | Shutting down local stack |
aegra db upgrade | Apply migrations | Multi-pod deployments before rolling out |
| Section | Purpose | Example |
|---|---|---|
graphs | Register LangGraph agents | {"agent": "./src/graph.py:graph"} |
auth | Enable authentication | {"path": "./auth.py:auth"} |
http | Custom FastAPI routes | {"app": "./routes.py:app"} |
store | Semantic search with embeddings | {"index": {"dims": 1536, "embed": "openai:text-embedding-3-small"}} |
checkpointer | Thread TTL and durability | {"ttl": {"strategy": "delete", "default_ttl": 43200}} |
builder.compile() result, cached once at startupconfig and/or ServerRuntime, called per-request for user-specific customization| Variable | Purpose |
|---|---|
DATABASE_URL | PostgreSQL connection (production) |
POSTGRES_* | Individual database fields (dev) |
REDIS_BROKER_ENABLED | Enable Redis for job dispatch (production) |
REDIS_URL | Redis connection string |
AEGRA_CONFIG | Path to custom config file |
AEGRA_CHECKPOINT_DURABILITY | Default checkpoint mode: sync, async, or exit |
AEGRA_THREAD_TTL | Thread expiry in minutes |
RUN_MIGRATIONS_ON_STARTUP | Auto-run migrations (set false for multi-pod) |
| Scenario | Command | Why |
|---|---|---|
| Local development | aegra dev | Starts PostgreSQL automatically, hot reload, simplest setup |
| Self-hosted production | aegra up | Full stack in Docker, includes Redis for scaling |
| PaaS (Railway, Render, Fly.io) | aegra serve | No Docker needed, you provide PostgreSQL + Redis |
| Kubernetes | aegra serve in pod spec | Stateless, works with managed databases |
| Single-instance production | aegra serve with REDIS_BROKER_ENABLED=false | Simpler, no Redis needed |
| Need | Use | Example |
|---|---|---|
| Simple agent, same for all users | Static graph | builder.compile() |
| Customize tools per user | Factory with ServerRuntime | Check runtime.user.permissions to add/remove tools |
| Manage resources (MCP, DB connections) | Factory with async context manager | Allocate resources at factory time, release on cleanup |
| Access user data in nodes | Runtime[T] parameter on node | Receive typed context via runtime.context |
| Mode | Use when | Example |
|---|---|---|
values | You need full state snapshots | Updating UI with complete conversation state |
updates | You only want state deltas | Efficient incremental updates |
messages | You're building a chat UI | Token-by-token LLM output |
custom | You emit domain-specific data | Progress updates, intermediate results |
events | You need fine-grained tracing | Debugging, detailed execution logs |
| Mode | Checkpoint timing | Recovery behavior | Use when |
|---|---|---|---|
sync | After every step | Resumes from last completed step | You need precise recovery, can tolerate latency |
async (default) | Background after each step | Resumes from last finished checkpoint | Standard production use |
exit | Once at run end | Restarts from thread's pre-run state | High-volume runs, no mid-run recovery needed |
Visit http://localhost:2026/docs to explore the API.
Write your graph in src/agent/graph.py:
Aegra automatically creates a default assistant for each graph.
Create auth.py:
Update aegra.json:
Docker (self-hosted):
PaaS (Railway, Render, Fly.io):
DATABASE_URL, REDIS_URL, REDIS_BROKER_ENABLED=trueaegra serve --host 0.0.0.0 --port $PORTKubernetes:
aegra serve as container commandGET /readyGET /liveGraph not loading — Check that the import path in aegra.json is correct and the variable is actually exported. Relative paths are resolved from the config file's directory.
Auth not working — Verify the auth handler is registered with @auth.authenticate and raises exceptions to reject requests (don't return a guest user to mean "no").
Migrations hang on startup (Kubernetes) — Multiple pods race for Alembic's advisory lock. Set RUN_MIGRATIONS_ON_STARTUP=false and run aegra db upgrade once per release from a Helm pre-upgrade Job.
Checkpoint durability confusion — exit mode only writes one checkpoint per run, so you can't inspect or fork from steps inside the run. Use sync or async if you need mid-run recovery or time travel.
Thread TTL not working — TTL applies only to threads created after it's enabled. Pre-existing threads are not backfilled. Threads with pending/running runs are skipped and swept later.
Custom routes not authenticated — By default, custom FastAPI routes don't require auth. Set enable_custom_route_auth: true in aegra.json to enforce auth on all custom routes.
Streaming reconnection lost — Track the last event ID you received and reconnect with the Last-Event-ID header. Events are retained for 10 minutes after the run completes.
Factory graph called multiple times — Factory graphs are called once at startup (for schema extraction) and once per request (for execution). Use runtime.execution_runtime to detect which context you're in.
Metadata merging on update — When updating an assistant, metadata merges into existing metadata (doesn't replace). Use config or context if you need to clear fields.
User isolation missing — When auth is enabled, threads are scoped to the authenticated user. Without auth, all users share the anonymous identity and see each other's threads.
Before deploying or submitting work:
aegra.json is valid JSON and all graph import paths exist.env file or platform config)aegra dev or aegra serve starts without connection errorsaegra db currenthttp://localhost:2026/docs and check /assistants endpointPOST /threads returns a thread with thread_idPOST /threads/{id}/runs returns a run with run_idGET /threads/{id}/runs/{id}/stream returns SSE eventsGET /threads/{id}/stateaegra.json and accessibleGET /health, GET /ready, GET /live all return 200REDIS_BROKER_ENABLED=trueRUN_MIGRATIONS_ON_STARTUP=false and migrations run out-of-bandFull documentation navigation: https://docs.aegra.dev/llms.txt
Critical pages:
For additional documentation and navigation, see: https://docs.aegra.dev/llms.txt
from langgraph.graph import StateGraph
from langgraph.types import Command, interrupt
# Define state, nodes, edges
builder = StateGraph(State)
builder.add_node("agent", call_model)
builder.add_node("tools", tool_node)
# ... wire up edges
graph = builder.compile(){
"graphs": {
"agent": "./src/agent/graph.py:graph"
}
}from langgraph_sdk import get_client
client = get_client(url="http://localhost:2026")
thread = await client.threads.create()
async for chunk in client.runs.stream(
thread_id=thread["thread_id"],
assistant_id="agent",
input={"messages": [{"type": "human", "content": "Hello"}]},
):
print(chunk)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("Missing token")
# Verify token (JWT, OAuth, etc.)
return {"identity": "user123", "display_name": "Alice"}{
"auth": {
"path": "./auth.py:auth"
}
}aegra up