AgentifUI Architecture
Scope
This document describes the current repository architecture, updated November 15, 2026. It is intentionally narrower than a long-term vision document: the goal is to explain how the codebase works today. For local setup, source navigation and change examples, use the developer guide; for submission rules, use Contributing.
Repository Shape
AgentifUI is a small monorepo with three main runtime areas:
| Area | Path | Role |
|---|---|---|
| Web app | app/, components/, lib/ |
Next.js UI, auth handlers, server utilities, route compatibility layer |
| API sidecar | apps/api/ |
Fastify server for business APIs and proxy-heavy internal routes |
| Shared package | packages/shared/ |
Cross-runtime helpers used by multiple packages |
Runtime Topology
Browser
-> Next.js App Router
- pages / layouts / SSR
- better-auth endpoints
- selected compatibility route handlers
-> selected /api/* rewrites
-> Fastify sidecar (apps/api)
-> PostgreSQL
-> Redis
-> MinIO / S3
-> Dify or compatible LangChain services
The split is deliberate:
- Next.js owns UI rendering, auth entrypoints, and a small number of route handlers that must stay in the App Router.
- Fastify owns the increasingly centralized business API surface, especially routes that need explicit envelopes, shared proxy policy, and stronger operational control.
Current Request Boundaries
Routes that stay in Next.js
These continue to execute in the App Router layer:
app/api/auth/better/[...all]/route.tsapp/api/auth/sso/providers/route.tsapp/api/sso/[providerId]/*app/api/internal/auth/*- compatibility stubs such as
app/api/internal/data/route.tsandapp/api/internal/error-events/client/route.ts
Those compatibility stubs are intentional. When Fastify proxying is disabled, they return explicit 503 envelopes instead of silently serving stale logic.
Routes that are normally served by Fastify
When FASTIFY_PROXY_ENABLED is on, Next.js rewrites selected prefixes to Fastify. The current default prefix list is defined in:
next.config.tsapps/api/src/config.ts
The active list includes:
/api/dify/api/content/api/internal/data/api/internal/apps/api/internal/profile/api/internal/error-events/client/api/internal/realtime/api/internal/storage/api/internal/ops/dify-resilience/api/internal/dify-config/api/internal/fastify-health/api/admin/api/translations
Guard scripts are part of the architecture here. They prevent drift between the Next rewrite layer, the Fastify route contract, and the disabled Next compatibility handlers.
Data Access Pattern
The repository uses PostgreSQL as the primary source of truth.
Next.js side
Most Next.js server-side data access lives under lib/:
lib/db/contains direct DB-oriented modules for business entities.lib/server/pg/manages connection pooling and session-scoped actor context.lib/services/contains higher-level orchestration around Dify, caching, content flows, and admin logic.
Fastify side
Fastify routes live in apps/api/src/routes/ and use apps/api/src/lib/ plus shared repository logic from the main app. The sidecar centralizes:
- route-level error envelopes
- proxy policy
- auth/session lookup for proxied requests
- storage and realtime route behavior
- operational health and observability endpoints
Agent Execution And Shared Interaction
The shared product API under /api/internal/apps/:appId/agent is registered in
apps/api/src/routes/agent-runs.ts, within the existing /api/internal/apps
rewrite prefix. It resolves application access and trusted local conversation/run
bindings before backend execution. It is not an unauthenticated pass-through to
an arbitrary Agent URL or a promise of a standard AG-UI HTTP endpoint.
apps/api/src/lib/agent-app.tsand the submission/run repositories resolve backend configuration, actor access, admission and persistent identities.apps/api/src/lib/agui/adapts the bounded negotiated Dify/LangChain paths;apps/api/src/lib/langchain/owns native LangChain transport and the optional human-input contract. Native service differences stay behind these boundaries.packages/shared/src/agui/holds event schemas/writing and pure display/history projections. It has no database or backend credentials. Schema-valid upstream data is not automatically safe public output.components/langchain/agent-chat.tsxintegrates shared product chat components and history. The directory name does not make the negotiated Dify path a second navigation or persistence system.
LangChain and explicitly opted-in Dify marketplace chatbots use negotiated AG-UI with server-managed saving. The Dify opt-in is text-only; existing Dify model, chatflow, workflow and other compatible paths retain their own behavior and browser-managed saving where applicable. A conversation must not have both a browser and server history writer.
backend_conversations, backend_runs, agent_submissions and
backend_run_interactions complement the existing local conversations/messages.
They track native bindings, execution versus saving, task admission and optional
approve/reject decisions. Backend identifiers supplied by the browser never replace
actor-scoped ownership checks. A disconnect is not cancellation, a known saved
result can support save-only recovery, and an unknown dispatch/resume must not
be blindly replayed. There is no general process-crash stream recovery.
See AG-UI integration, LangChain integration, Dify integration and generation governance for exact negotiated profiles, capabilities, migration requirements and failure semantics.
PostgreSQL Security Model
The project has moved to local PostgreSQL ownership and no longer depends on Supabase tables or auth.users.
Key runtime ideas:
- RLS is enabled on the main business tables through SQL migrations.
- The application sets PostgreSQL GUCs such as
app.current_user_id,app.current_user_role, andapp.rls_system_actorper request. APP_RLS_STRICT_MODE=1tightens the legacy bypass behavior so only explicit system-actor contexts can bypass actor checks.
Relevant implementation files:
lib/server/pg/pool.tslib/server/pg/session-options.tslib/server/pg/user-context.tsdatabase/migrations/20260215030000_m4_rpc_rls_guc_hardening.sqldatabase/migrations/20260215050000_m4_table_rls_phase2.sqldatabase/migrations/20260215070000_m4_table_rls_phase3.sqldatabase/migrations/20260215080000_m4_rls_strict_mode_switch.sql
Auth and Identity Model
Authentication is provided by better-auth, backed by PostgreSQL tables in public:
auth_usersauth_sessionsauth_accountsauth_verificationsauth_sso_providers
auth_sso_providers belongs to the Better Auth SSO plugin and is separate from
the application’s sso_providers table. See database design
for the corresponding migration and table roles.
Business-facing profile and identity data are separate:
profilesuser_identitiesprofile_external_attributessso_providersdomain_sso_mappingsauth_settings
This split lets the app keep auth persistence, business profile state, and external IdP metadata distinct.
Realtime and Caching
Realtime is currently anchored on a database outbox pattern.
database/migrations/20260215170000_m6_realtime_outbox_cdc.sqlcreatesrealtime_outbox_eventsand triggers on key business tables.- Redis is used for cache invalidation and broker-style helpers.
- When realtime-sensitive prefixes are proxied through Fastify,
REALTIME_SOURCE_MODEmust staydb-outbox.
This rule is enforced at Fastify startup in apps/api/src/server.ts.
Storage
Object storage uses MinIO or another S3-compatible backend.
Current public storage flows include:
- avatar upload and presign endpoints
- content-image upload and presign endpoints
- optional trusted-avatar host filtering
Relevant code paths:
app/api/internal/storage/*apps/api/src/routes/internal-storage-*lib/services/content-image-upload-service.tslib/utils/profile-cache-security.ts
Error Handling and Observability
The architecture standardizes error envelopes across Next.js and Fastify.
Important pieces:
- shared request-id propagation
- compatibility normalization of legacy errors
- persisted API/frontend error events in
error_events - smoke checks for production routes
- guard scripts for route and envelope consistency
The recent /api/internal/error-events/client cutover is an example of this pattern: Fastify serves the live route, while the Next.js file remains as an explicit disabled stub for cutover-off mode.
Deployment Model
Two application installation paths share the same Web/API and data boundaries:
- Host/PM2:
AgentifUI-Prodruns Next.js andAgentifUI-API-Prodruns Fastify. The deployment entrypoint ispnpm run deploy, withecosystem.prod.config.jsandscripts/smoke-prod.sh. - Docker: one application image runs separate Web/API services through
compose.yaml; data services can use the optional profile or external dependencies. The optional manual-capacity topology manages replicas through its own gateway/connection-pool configuration, not ordinary bridge scaling.
Both require PostgreSQL, Redis, S3-compatible storage and persistent runtime content outside replaceable releases. Environment configuration, native backend credentials and user/operational data are not part of the image/source defaults. Public Web settings and prepared interface translations are build inputs; database startup does not automatically apply migrations.
Follow host deployment, Docker, manual Docker capacity, configuration and operations for commands. Ordinary feature Stage acceptance uses a separate PM2 production build; relevant Docker validation is additional for deployment/image/startup changes and Docker deliveries. Private environment topology is not a prerequisite for a public contributor’s local setup.
CI Model
GitHub Actions currently runs on pushes and pull requests for both main and develop.
The pipeline does the following:
- check the outgoing publication for secrets and private paths on every run
- skip heavy checks for documentation-only changes and cancel superseded runs
- install dependencies once per quality or test/build job with pnpm caching
- run format/lint checks on changed files
- run root and workspace type checks
- start isolated PostgreSQL, Redis, and MinIO services on the GitHub runner
- apply SQL migrations
- build the shared package before type checks and tests that import it, then build the API and web packages (or validate the full application in Docker)
Dependency auditing runs separately on a weekly schedule or manual request. It does not duplicate installation on every push or pull request. Build outputs are not uploaded because deployment builds from the selected source version with its own environment configuration.
This matters architecturally because the repository now assumes monorepo-aware CI and explicit service startup for build/test verification.
Architectural Invariants
These are the current rules worth preserving:
- Fastify and Next route boundaries must stay explicit.
- Prefix lists for rewrites and Fastify registration must stay in sync.
- Business data should flow through PostgreSQL with RLS-aware actor context.
- Test and CI environments should use isolated database, Redis, and bucket namespaces.
- Disabled compatibility routes should fail closed, not silently serve stale business logic.
- Shared interaction does not erase backend capability differences or authorize arbitrary upstream metadata/tool output for the browser.
- A conversation has one persistence owner; execution, saving and human decisions retain trusted local identities and explicit unknown-outcome handling.