d5b122d4a3
CI / lint (push) Successful in 14s
CI / quality (push) Successful in 21s
CI / security (push) Successful in 36s
CI / build (push) Successful in 36s
CI / typecheck (push) Successful in 39s
CI / benchmark-regression (push) Has been skipped
CI / unit_tests (push) Successful in 2m57s
CI / integration_tests (push) Successful in 3m23s
CI / docker (push) Successful in 53s
CI / coverage (push) Successful in 5m58s
CI / benchmark-publish (push) Successful in 19m27s
229 lines
14 KiB
Markdown
229 lines
14 KiB
Markdown
---
|
|
adr_number: 48
|
|
title: "Server Application Architecture"
|
|
status_history:
|
|
- ["2026-03-11", "Draft", "Jeffrey Phillips Freeman"]
|
|
- ["2026-03-11", "Proposed", "Jeffrey Phillips Freeman"]
|
|
- ["2026-03-11", "Accepted", "Jeffrey Phillips Freeman"]
|
|
tier: 4
|
|
authors: ["Jeffrey Phillips Freeman"]
|
|
superseded_by:
|
|
related_adrs:
|
|
- number: 1
|
|
title: "Layered Architecture"
|
|
relationship: "The server shares the same Domain and Application layers as the client; only Infrastructure and Presentation layers differ"
|
|
- number: 22
|
|
title: "LangChain/LangGraph Integration"
|
|
relationship: "The server deploys actor StateGraphs to LangGraph Platform and invokes them via RemoteGraph"
|
|
- number: 23
|
|
title: "Server Mode"
|
|
relationship: "Refines ADR-023's server mode design with concrete architecture — LangGraph Platform for execution, A2A standard for protocol, PostgreSQL for persistence"
|
|
- number: 26
|
|
title: "Agent-to-Agent Protocol (A2A)"
|
|
relationship: "The server implements the A2A standard endpoint; ADR-026 defines the protocol boundary role"
|
|
- number: 47
|
|
title: "A2A Standard Adoption"
|
|
relationship: "ADR-047 defines the protocol the server speaks (A2A standard + extensions); this ADR defines the server that implements it"
|
|
acceptance:
|
|
votes_for:
|
|
- voter: "Jeffrey Phillips Freeman <Jeffrey.Freeman@CleverThis.com>"
|
|
comment: "A separate server application sharing domain/application layers with the client, backed by LangGraph Platform for remote execution, provides clean separation of concerns while maximizing code reuse"
|
|
votes_against: []
|
|
abstentions: []
|
|
---
|
|
## Context
|
|
|
|
ADR-023 established that CleverAgents supports local and server deployment modes sharing Domain and Application layers, differing only in Infrastructure and Presentation. ADR-047 adopted the A2A standard as the sole client-server protocol. What remains unspecified is the concrete architecture of the server application itself — how it is structured, how it executes actor graphs remotely, how it manages multi-user state, and how it integrates with the shared layers.
|
|
|
|
## Decision Drivers
|
|
|
|
- The server must share the same Domain and Application layers as the client to avoid behavioral drift (ADR-001, ADR-023)
|
|
- Remote actor graph execution must not limit plan capabilities — different actors for different plan phases (strategy, execution) must each deploy independently
|
|
- The server must speak the A2A standard protocol (ADR-047) as its sole client-facing interface
|
|
- Multi-user persistence requires PostgreSQL (not SQLite) with proper isolation and namespace-scoped authorization
|
|
- The server must be deployable to Kubernetes via Helm charts for production environments
|
|
- LangGraph Platform is the established graph execution engine (ADR-022) and provides RemoteGraph for remote invocation
|
|
|
|
## Decision
|
|
|
|
The CleverAgents server is a **separate application** (distinct repository/deployment unit) that shares the same Domain and Application layers as the client. It implements the A2A standard endpoint (JSON-RPC 2.0 over HTTP) for all client communication, delegates actor graph execution to **LangGraph Platform** via **RemoteGraph**, and uses PostgreSQL for multi-user persistence.
|
|
|
|
## Design
|
|
|
|
### Server Application Structure
|
|
|
|
The server is organized following the same four-layer architecture as the client (ADR-001), sharing two layers and providing its own implementations for the other two:
|
|
|
|
| Layer | Client | Server | Shared? |
|
|
|-------|--------|--------|---------|
|
|
| **Domain** | Business rules, domain models, domain events | Same | Yes — identical package |
|
|
| **Application** | Service facades, workflows, event bus | Same | Yes — identical package |
|
|
| **Infrastructure** | SQLite, local sandbox, local file access, `A2aLocalFacade` | PostgreSQL, LangGraph Platform RemoteGraph, server-side sandbox, A2A SDK server | No — different implementations |
|
|
| **Presentation** | CLI (Typer), TUI (Textual), IDE plugin | A2A JSON-RPC 2.0 endpoint | No — different entry points |
|
|
|
|
The shared Domain and Application layers are consumed as a Python package dependency. This ensures zero behavioral drift between local and server modes.
|
|
|
|
### Presentation Layer: A2A Endpoint
|
|
|
|
The server's Presentation layer is an **A2A JSON-RPC 2.0 endpoint** implemented using the A2A Python SDK's server-side connection handler. This is the **only** client-facing interface — there is no REST API, no GraphQL, no separate admin endpoint.
|
|
|
|
The endpoint handles:
|
|
|
|
- **Standard A2A methods**: `message/send`, `message/stream` — routed to `SessionWorkflow` and actor execution via Tasks
|
|
- **Extension methods**: `_cleveragents/*` — routed to Application-layer services (PlanService, RegistryServices, SyncService, etc.)
|
|
- **Multi-turn interaction**: Tasks entering `input-required` state prompt the connected client for decisions or permissions
|
|
|
|
### Infrastructure Layer: LangGraph Platform
|
|
|
|
Actor graphs (StateGraphs defined in YAML) are **deployed to LangGraph Platform** as separate deployments. The server invokes them via **RemoteGraph**:
|
|
|
|
```
|
|
Client ──A2A──→ Server A2A Endpoint
|
|
│
|
|
├── SessionWorkflow (Application Layer)
|
|
│ │
|
|
│ └── RemoteGraph.invoke(actor_graph, state)
|
|
│ │
|
|
│ └── LangGraph Platform (remote actor execution)
|
|
│ ├── Strategy Actor Graph
|
|
│ ├── Execution Actor Graph
|
|
│ └── Estimation Actor Graph
|
|
│
|
|
└── Extension Method Handlers
|
|
├── PlanService
|
|
├── RegistryServices
|
|
├── SyncService
|
|
└── NamespaceService
|
|
```
|
|
|
|
This does **not** limit plan execution. Different actors for different plan phases (strategy vs. execution vs. estimation) each deploy as **separate RemoteGraphs**. The `SessionWorkflow` orchestrates them the same way it orchestrates local actor graphs, but through the RemoteGraph interface instead of direct in-process invocation.
|
|
|
|
### Infrastructure Layer: Persistence
|
|
|
|
| Component | Technology | Notes |
|
|
|-----------|-----------|-------|
|
|
| Database | PostgreSQL via SQLAlchemy | Same ORM as client (SQLite), different dialect |
|
|
| Migrations | Alembic | Schema migrations for server-specific tables (users, tokens, namespace ACLs) |
|
|
| Session store | PostgreSQL | Multi-user session persistence with namespace isolation |
|
|
| Plan store | PostgreSQL | Plan lifecycle records, decision trees, artifacts |
|
|
|
|
The SQLAlchemy repository implementations are shared between client and server where the schema is identical. Server-specific tables (authentication, namespace ACLs, user management) are additive.
|
|
|
|
### Infrastructure Layer: Additional Components
|
|
|
|
| Component | Technology | Purpose |
|
|
|-----------|-----------|---------|
|
|
| Logging | structlog | Structured logging consistent with client (ADR-025) |
|
|
| Caching | Redis (optional) | Multi-instance session affinity, rate limiting. Single-instance deployments can use in-memory caching. |
|
|
| Task queue | None (synchronous) | Plans execute synchronously via RemoteGraph. No background job queue — the A2A connection stays open during execution with streaming notifications. |
|
|
|
|
### Security Architecture
|
|
|
|
**Authentication flow:**
|
|
|
|
1. Client discovers the server's Agent Card (via `/.well-known/agent.json`) which declares supported auth schemes and `_cleveragents` extensions
|
|
2. Client authenticates using the declared HTTP auth scheme (e.g., Bearer token via `Authorization: Bearer <server.token>`)
|
|
3. Server validates the token against its user/token store
|
|
4. All subsequent requests include the auth credentials; the `A2A-Version` header identifies the protocol version
|
|
|
|
**Authorization:**
|
|
|
|
- Namespace-scoped: users can only access entities in namespaces they have been granted access to
|
|
- The `local/` namespace is never accessible via server — it exists only on the client
|
|
- Server namespaces (`<username>/`, `<orgname>/`) have explicit ACLs
|
|
|
|
**Transport security:**
|
|
|
|
- HTTPS required for all production deployments (TLS termination at ingress)
|
|
- The A2A SDK HTTP transport supports standard TLS
|
|
|
|
### Deployment Architecture
|
|
|
|
| Component | Technology | Notes |
|
|
|-----------|-----------|-------|
|
|
| Container | Docker | Server packaged as container image |
|
|
| Orchestration | Kubernetes | Production deployment target |
|
|
| Configuration | Helm chart (`k8s/`) | Configurable replicas, resource limits, ingress |
|
|
| Ingress | Kubernetes Ingress | TLS termination, routing |
|
|
| Database | PostgreSQL (managed or self-hosted) | External to the application container |
|
|
| LangGraph Platform | Managed or self-hosted | External service for graph execution |
|
|
|
|
### Sync Protocol
|
|
|
|
Entity synchronization between client and server uses A2A extension methods:
|
|
|
|
| Method | Purpose |
|
|
|--------|---------|
|
|
| `_cleveragents/sync/pull` | Download server namespace entities to local cache |
|
|
| `_cleveragents/sync/push` | Upload local entity definitions to a server namespace |
|
|
| `_cleveragents/sync/status` | Compare local and server entity versions |
|
|
|
|
Sync behavior:
|
|
|
|
- **Auto-sync** (`server.sync.auto`): Entities sync on connection and at `server.sync.interval` (default: 300s)
|
|
- **Direction**: Server → local for server namespaces; local → server only on explicit push
|
|
- **Conflict resolution**: Server version wins by default; conflicts are surfaced to the user via `sync/status`
|
|
- The `local/` namespace is **never** synced
|
|
|
|
### Technology Stack
|
|
|
|
| Component | Version | Purpose |
|
|
|-----------|---------|---------|
|
|
| Python | >= 3.13 | Runtime |
|
|
| A2A Python SDK (`a2a-sdk`) | Latest | A2A JSON-RPC 2.0 server implementation |
|
|
| LangGraph Platform | Latest | Remote actor graph execution |
|
|
| PostgreSQL | >= 15 | Multi-user persistence |
|
|
| SQLAlchemy | >= 2.0 | ORM (shared with client) |
|
|
| Alembic | >= 1.13 | Database migrations |
|
|
| structlog | >= 24.1 | Structured logging |
|
|
| Redis | >= 7.0 (optional) | Caching, session affinity for multi-instance |
|
|
| Helm | >= 3.0 | Kubernetes deployment |
|
|
|
|
## Constraints
|
|
|
|
- The server **must not** contain any Domain or Application layer code that is not shared with the client. All mode differences must be in Infrastructure and Presentation layers.
|
|
- The server's **only** client-facing interface is the A2A JSON-RPC 2.0 endpoint. No REST API, no GraphQL, no admin endpoints.
|
|
- Actor graph execution **must** go through LangGraph Platform RemoteGraph — the server does not execute actor graphs in-process.
|
|
- The `local/` namespace must never be accessible or syncable via the server.
|
|
- All A2A connections require authentication (via HTTP auth schemes declared in the Agent Card) before any task operations.
|
|
|
|
## Consequences
|
|
|
|
### Positive
|
|
|
|
- Maximum code reuse — Domain and Application layers are identical, eliminating behavioral drift between local and server modes
|
|
- LangGraph Platform provides production-grade graph execution with built-in scaling, persistence, and monitoring
|
|
- The A2A-only interface ensures all clients (CLI, TUI, IDE, third-party) interact identically with the server
|
|
- RemoteGraph per actor enables independent scaling of different actor types (strategy actors may need different resources than execution actors)
|
|
- PostgreSQL provides proven multi-user persistence with proper transaction isolation
|
|
|
|
### Negative
|
|
|
|
- Dependency on LangGraph Platform — the server cannot execute actors without it
|
|
- Operating a server requires PostgreSQL, Kubernetes, and optionally Redis — significantly more infrastructure than local mode
|
|
- The A2A Python SDK is relatively new; production edge cases may require SDK contributions or workarounds
|
|
|
|
### Risks
|
|
|
|
- LangGraph Platform availability directly affects server plan execution. Mitigation: health checks and graceful degradation (report unavailability, don't silently fail)
|
|
- Network latency between server and LangGraph Platform adds overhead to every actor invocation. Mitigation: co-locate server and LangGraph Platform in the same region/cluster
|
|
- The shared Domain/Application package must be versioned carefully to avoid incompatibilities between client and server releases. Mitigation: semantic versioning with backward compatibility guarantees
|
|
|
|
## Alternatives Considered
|
|
|
|
**Single monolithic server (no LangGraph Platform)** — Execute actor graphs in the server process directly. Simpler deployment but loses LangGraph Platform's scaling, persistence, and monitoring. Doesn't align with ADR-022's adoption of LangGraph. Rejected.
|
|
|
|
**Server in the same repository as the client** — Simpler development workflow but conflates deployment units and makes it harder to version the shared layers independently. A separate application with shared package dependencies is cleaner. Rejected.
|
|
|
|
**Multiple API surfaces (A2A + REST admin)** — An admin REST API alongside A2A for server management. Adds a second protocol surface, complicates authentication, and violates the single-protocol principle. Admin operations are handled via `_cleveragents/` extension methods. Rejected.
|
|
|
|
## Compliance
|
|
|
|
- **Shared layer tests**: Tests verify that Domain and Application layers imported by the server are byte-identical to those used by the client (same package version).
|
|
- **A2A conformance tests**: The server passes the A2A standard test suite for all standard methods.
|
|
- **Extension method tests**: All `_cleveragents/` methods are tested against documented parameter and response schemas.
|
|
- **RemoteGraph integration tests**: Tests verify actor graph deployment to and invocation via LangGraph Platform.
|
|
- **Authentication tests**: Tests verify the `authenticate` flow and namespace-scoped authorization.
|
|
- **Sync tests**: Integration tests verify pull/push/status sync operations including conflict detection.
|
|
- **Deployment tests**: Helm chart renders correctly and the container starts with valid configuration.
|