Skip to main content

Database

All persistent data lives in one relational database selected by DB_URL in backend/.env:

BackendDB_URLNotes
SQLite (default)sqlite:///a2flow.dbZero-config local file
PostgreSQLpostgresql://user:password@host:5432/a2flowUsed by the Docker Compose stack

The async driver suffix (aiosqlite / asyncpg) is added automatically. Schema changes are tracked as versioned Alembic migrations and applied automatically on startup — redeploying the app (a container restart) is what runs any pending migrations, so no separate migration step is needed.

The database is also what coordinates a multi-replica backend: an agent run holds a PostgreSQL advisory lock for the length of its SSE stream, so one conversation is never driven by two replicas at once. See Horizontal scaling for what that protects and the constraint it places on connection pooling.

What it holds​

TableHolds
usersApplication users and the roles granted to them — see Users and groups
auth_sessionsServer-side login sessions
login_failuresRecent failed sign-ins, counted to lock an account against repeated guessing; rows older than a day are removed
impersonation_eventsAudit trail of impersonation sessions
agent_skillsAgent Skill definitions and the repository each is cloned from
mcp_serversRegistered MCP servers and how to reach them
knowledge_bases, knowledge_sources, knowledge_pagesKnowledge bases: each one's uploaded documents and registered URLs, and the pages written from them
secretsNamed credential bundles, either encrypted locally or referenced in Vault
workflowsWorkflow definitions and their lifecycle status
workflow_task_templatesThe pre-designed task list of a workflow
workflow_published_versionsThe design frozen at publish time, which a modified workflow runs against
workflow_executionsOne row per run, with the workflow and skill metadata snapshotted at execute time
workflow_tasksThe individual tasks of a run, copied from the templates
workflow_task_tool_bindingsThe MCP tools bound to a task
message_metaPer-message facts for the two shared chats: who sent a message, and which task was in progress

Deleting a workflow cascades to its templates but leaves its past runs intact — a run is a record of what happened, not part of the design it came from.