Files
cleveragents-core/docs/adr/ADR-048-server-application-architecture.md
freemo 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
Docs: Updated to A2A and integrating rest standard
2026-03-11 13:19:55 -04:00

14 KiB

adr_number, title, status_history, tier, authors, superseded_by, related_adrs, acceptance
adr_number title status_history tier authors superseded_by related_adrs acceptance
48 Server Application Architecture
2026-03-11
Draft
Jeffrey Phillips Freeman
2026-03-11
Proposed
Jeffrey Phillips Freeman
2026-03-11
Accepted
Jeffrey Phillips Freeman
4
Jeffrey Phillips Freeman
number title relationship
1 Layered Architecture The server shares the same Domain and Application layers as the client; only Infrastructure and Presentation layers differ
number title relationship
22 LangChain/LangGraph Integration The server deploys actor StateGraphs to LangGraph Platform and invokes them via RemoteGraph
number title relationship
23 Server Mode Refines ADR-023's server mode design with concrete architecture — LangGraph Platform for execution, A2A standard for protocol, PostgreSQL for persistence
number title relationship
26 Agent-to-Agent Protocol (A2A) The server implements the A2A standard endpoint; ADR-026 defines the protocol boundary role
number title relationship
47 A2A Standard Adoption ADR-047 defines the protocol the server speaks (A2A standard + extensions); this ADR defines the server that implements it
votes_for votes_against abstentions
voter comment
Jeffrey Phillips Freeman <Jeffrey.Freeman@CleverThis.com> 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

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.