Aegra Skill
Product summary
Aegra is an open-source, self-hosted Agent Protocol server for running LangGraph agents on your own infrastructure. It provides a drop-in replacement for LangSmith Deployments with the same SDK, full persistence via PostgreSQL, streaming via SSE, human-in-the-loop approval gates, semantic storage with pgvector, and flexible authentication (JWT, OAuth, Firebase, or custom). Key files: aegra.json (configuration), .env (environment variables), Dockerfile and docker-compose.yml (deployment). 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 locally, on Docker, PaaS (Railway, Render), or Kubernetes
- Managing assistants — Creating, versioning, searching, or updating configured graph instances
- Building conversations — Creating threads, managing state, accessing checkpoint history, searching by metadata
- Streaming responses — Configuring stream modes, handling SSE reconnection, implementing background runs
- Adding authentication — Implementing JWT, OAuth, Firebase, or custom auth handlers with authorization rules
- Human-in-the-loop — Adding approval gates, tool review, or user intervention points in agent execution
- Persistent storage — Using key-value or semantic (vector) storage for conversation memory, knowledge bases, or user preferences
- Observability — Configuring tracing to Langfuse, Phoenix, or generic OTLP backends
- Custom routes — Adding FastAPI endpoints alongside the Agent Protocol API
Quick reference
CLI commands
| Command | Use case | Starts DB? | Starts app? |
|---|
aegra init | Create new project | — | — |
aegra dev | Local development | Yes (Docker) | Yes (host, hot reload) |
aegra up | Self-hosted Docker production | Yes (Docker) | Yes (Docker) |
aegra serve | PaaS, containers, bare metal | No | Yes (host) |
aegra down | Stop Docker services | — | — |
Configuration files
| File | Purpose |
|---|
aegra.json | Define graphs, auth, HTTP routes, semantic store |
.env | Database, Redis, LLM keys, logging, observability |
Dockerfile | Container image for deployment |
docker-compose.yml | PostgreSQL + app (dev/prod) or PostgreSQL + Redis + app (prod) |
Core API patterns
# Initialize client
from langgraph_sdk import get_client
client = get_client(url="http://localhost:2026")
# Assistants (configured graph instances)
assistant = await client.assistants.create(graph_id="agent", name="My Agent")
assistants = await client.assistants.search(graph_id="agent")
await client.assistants.update(assistant_id, name="Updated")
# Threads (conversations with persistent state)
thread = await client.threads.create(metadata={"user": "alice"})
state = await client.threads.get_state(thread_id)
await client.threads.update_state(thread_id, values={...})
# Runs (agent executions)
async for chunk in client.runs.stream(thread_id, assistant_id, input={...}):
print(chunk)
run = await client.runs.create(thread_id, assistant_id, input={...})
await client.runs.cancel(thread_id, run_id)
# Store (key-value + semantic)
await client.store.put_item(namespace=["users", "alice"], key="prefs", value={...})
items = await client.store.search_items(namespace_prefix=["users"], query="...", limit=10)
Environment variables (key ones)
| Variable | Default | Purpose |
|---|
DATABASE_URL | — | PostgreSQL connection (takes precedence over POSTGRES_*) |
POSTGRES_HOST, POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_PORT, POSTGRES_DB | localhost, user, password, 5432, aegra | Individual DB config |
REDIS_BROKER_ENABLED | false | Enable Redis for multi-instance SSE and worker job queue |
REDIS_URL | redis://localhost:6379/0 | Redis connection |
OPENAI_API_KEY | — | LLM provider key |
AUTH_TYPE | noop | noop (no auth) or custom |
OTEL_TARGETS | — | Observability: LANGFUSE, PHOENIX, GENERIC |
WORKER_COUNT | 3 | Number of worker loops per instance |
N_JOBS_PER_WORKER | 10 | Max concurrent runs per worker |
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 | Starts PostgreSQL + Redis + app in Docker with health checks and auto-restart |
| PaaS platform (Railway, Render, Fly.io) | aegra serve | No Docker orchestration; you provide managed PostgreSQL and Redis |
| Kubernetes | aegra serve in pod spec | Use managed PostgreSQL (CloudSQL, RDS) and Redis (ElastiCache) |
| Single-instance PaaS without Redis | aegra serve with REDIS_BROKER_ENABLED=false | Runs execute as in-process asyncio tasks; suitable for single instance |
When to use stream modes
| Mode | Use case |
|---|
values | Full state snapshot after each node (good for UI state updates) |
updates | Only state deltas (efficient for large state objects) |
messages | LLM tokens and tool calls with accumulation (best for chat UI) |
messages-tuple | Raw message tuples without accumulation (JavaScript compatibility) |
custom | User-defined data from get_stream_writer() in nodes |
events | Low-level LangGraph events for fine-grained tracing |
When to use static vs factory graphs
| Need | Use | Pattern |
|---|
| Simple graph, same for all users | Static graph | graph = builder.compile() in aegra.json |
| Customize graph per user (tools, models) | Factory graph | def graph(runtime: ServerRuntime):... returns compiled graph |
| Manage resources (MCP, DB connections) | Factory with async context manager | @asynccontextmanager async def graph(runtime):... |
| Access user context in nodes | Runtime[T] parameter | Add runtime: Runtime[MyContext] to node function |
Workflow
1. Create and run a new Aegra project
- Initialize project:
aegra init → choose template (simple-chatbot or react-agent) and location - Configure environment:
cp.env.example.env → add OPENAI_API_KEY and other secrets - Install dependencies:
uv sync (uses uv for dependency management) - Start dev server:
uv run aegra dev → PostgreSQL starts in Docker, app runs on host with hot reload - Verify: Visit http://localhost:2026/docs to see API docs
- Test: Use LangGraph SDK client to create threads and run agents
2. Deploy to production with Docker
- Ensure aegra.json is configured with all graphs, auth, and routes
- Set environment variables in
.env (DATABASE_URL, OPENAI_API_KEY, AUTH_TYPE, etc.) - Build and start:
aegra up → generates docker-compose.yml if needed, builds image, starts PostgreSQL + Redis + app - Verify health: Check
GET /health, GET /ready, GET /live endpoints - Monitor: Logs from
docker compose logs -f show migrations, startup, and runtime errors
3. Add authentication
- Create auth handler (e.g.,
my_auth.py): from langgraph_sdk import Auth auth = Auth() @auth.authenticate async def authenticate(headers: dict) -> dict: token = headers.get("Authorization", "").replace("Bearer ", "") # Verify token (JWT, OAuth, Firebase, etc.) return {"identity": "user123", "permissions": ["read", "write"]} - Add to aegra.json:
"auth": {"path": "./my_auth.py:auth"} - Restart server:
aegra dev or aegra up - Access user in graph:
config["configurable"]["langgraph_auth_user"] in nodes or tools
4. Implement human-in-the-loop approval
- Add interrupt node to graph that calls
interrupt() with action details - Route through approval in conditional edges before tool execution
- Client checks thread status:
await client.threads.get_state(thread_id) → check interrupts field - Resume with command:
await client.runs.stream(..., command={"resume": [{"type": "accept", "args": None}]}) - Alternatively use interrupt_before/after: Pass
interrupt_before=["tools"] to run without modifying graph code
5. Configure semantic storage
- Add store section to aegra.json:
{"store": {"index": {"dims": 1536, "embed": "openai:text-embedding-3-small", "fields": ["$"]}}} - Store items:
await client.store.put_item(namespace=[...], key="...", value={...}) - Search semantically:
await client.store.search_items(namespace_prefix=[...], query="...", limit=10) - In graph nodes: Use
store: BaseStore parameter (auto-injected by LangGraph)
Common gotchas
- Install
aegra-cli, not aegra — The aegra package on PyPI is a convenience wrapper without version pinning. Always pip install aegra-cli. - DATABASE_URL takes precedence — If both
DATABASE_URL and individual POSTGRES_* variables are set, DATABASE_URL wins and POSTGRES_* are ignored. - Migrations run automatically — Don't run them manually. They apply on startup for all deployment methods.
- Redis is optional in dev, required in production —
aegra dev works without Redis (REDIS_BROKER_ENABLED=false). For multi-instance production, set REDIS_BROKER_ENABLED=true and provide a Redis URL. - Stream mode
debug is always enabled internally — You only receive debug events if you explicitly request them in stream_mode. Otherwise, only interrupt events are forwarded. - Interrupts use
command, not input — When resuming an interrupted run, pass command={"resume": [...]}, not input. These are mutually exclusive. - Store values must be JSON objects — Primitive values (strings, numbers) are rejected. Wrap them:
{"value": "text"}. - User isolation is automatic with auth — Threads and store items are scoped to the authenticated user. No manual filtering needed.
- Factory graphs get user context at build time —
ServerRuntime.user is available when the factory is called (for structural decisions). Execution-time user data goes in config["configurable"]["langgraph_auth_user"]. - Windows doesn't support
aegra serve — Use aegra dev or aegra up (Docker) on Windows. aegra serve requires Linux/macOS because psycopg needs SelectorEventLoop. - Health checks are critical in Docker — Generated docker-compose.yml includes health checks. If the app hangs, Docker marks it unhealthy and restarts it (with
restart: unless-stopped). - CORS defaults to allow all origins — Default is
allow_origins: ["*"] with allow_credentials: false. When you specify concrete origins, allow_credentials defaults to true automatically.
Verification checklist
Before submitting work with Aegra:
- Configuration:
aegra.json is valid JSON and all graph import paths exist - Environment:
.env has all required keys (DATABASE_URL or POSTGRES_*, OPENAI_API_KEY, etc.) - Database: Can connect to PostgreSQL; migrations have run (check logs for "Alembic" messages)
- Graphs: All graphs in
aegra.json load without import errors (check startup logs) - Auth: If auth is configured, test with valid and invalid tokens; verify user data is accessible in nodes
- Assistants: Default assistants created for each graph (one per graph ID in aegra.json)
- Threads: Can create threads, retrieve state, update state without errors
- Runs: Can stream runs, receive events, handle interrupts (if HITL is implemented)
- Store: Can put/get/search items; semantic search works if configured
- Streaming: Multiple stream modes work; SSE reconnection works (if Redis is enabled)
- Health endpoints:
/health, /ready, /live return 200 OK - Deployment: Docker image builds; containers start and stay healthy; migrations apply on startup
- Observability: Traces are exported to configured backend (Langfuse, Phoenix, etc.) if enabled
Resources
For additional documentation and navigation, see: https://docs.aegra.dev/llms.txt