Data model reference
For operators, contributors and integrators who need to know what Agent Taskbay stores, how records are identified, and which rules the database itself enforces.
> For operators, contributors and integrators who need to know what Agent Taskbay stores, how records are identified, and which rules the database itself enforces.
Source of truth: src/server/adapters/db/entities.ts and the migrations in src/server/adapters/db/migrations/. This page summarizes them; where it differs, the code is right. The schema
is not a public interface: tables and columns can change in any release (run migrations when you upgrade; see Upgrading and backups).
The same schema runs on embedded PGlite and on PostgreSQL. Table names are lower-case snake_case; the ORM is MikroORM and migrations are tracked in agent_taskbay_migrations.
Identity rules
| Rule | Detail |
|---|---|
| Local IDs | Every record has a UUID generated by Agent Taskbay before it is stored. URLs and APIs use these local IDs. |
| Tenancy | organization_id is the tenancy boundary and is on every domain table. Queries are always scoped by it, and by the caller's grants. |
| Remote task identity | (agent, tenant, remote task ID) (unique constraint uq_tasks_remote_identity). A remote task ID is never used on its own as a key or in a URL. |
| Remote context identity | (agent, tenant, remote context ID). |
| Remote message identity | (task, projection version, remote message ID) for stored message projections. |
| Direct messages | A direct reply with no task is stored as a task row with kind message and a null remote_task_id; it has a locally generated identity. |
| Workflow IDs | Ownership, notes and approvals are keyed by local task IDs. They are independent of A2A contextId and referenceTaskIds. |
| External identities | A person is a users row mapped by exact (issuer, subject) from the identity provider. E-mail and provider role or organization claims are never identity or authority. |
Different agents or tenants can use the same remote task ID without colliding, because the agent and tenant are part of the identity.
Entities
Organizations and agents
| Table | Purpose | Notable columns and constraints |
|---|---|---|
organizations | Tenancy root | slug unique, name. A bootstrap organization with slug local and name Local is created automatically; the catalog's environment seeds, development identity and legacy import belong to it. |
agents | An agent registered by Agent Card URL | card_url, source (env or managed), enabled, display_name, description, protocol_snapshot_version, last_discovery_at, last_healthy_at. Unique (organization_id, card_url). |
agent_card_snapshots | History of discovered cards | raw_card_json, normalized_card_json, compliance_json, signature_status (unsigned, verified, untrusted, invalid), digest, fetched_at, resolved_card_url. The latest snapshot decides which extensions are honored. |
agent_credentials | Encrypted credential binding for one agent | kind, ciphertext (compact JWE, AES-256-GCM), key_id, enabled, revision. Unique (organization_id, agent_id). The ciphertext authenticates the organization and agent. No plaintext is stored. |
Tasks, events and projections
| Table | Purpose | Notable columns and constraints |
|---|---|---|
tasks | One row per observed A2A task (or direct message) | agent_id, tenant, remote_task_id, remote_context_id, kind, state (stored as text, for example TASK_STATE_INPUT_REQUIRED), title, skill_id, projection_version, content_json (legacy projection), terminal_at, version (optimistic lock), local and remote timestamps. |
task_events | Append-only ledger of everything observed | sequence (database-generated feed cursor), source (stream, webhook, reconcile, command_response, import), event_kind, source_key, payload_digest, payload_json, session_id, request_id, trace_id, projection_version. Unique (task_id, source, source_key) makes ingestion idempotent. |
task_projections, message_projections, artifact_projections | Normalized views rebuilt from the ledger | Keyed by (task_id, projection_version); messages and artifacts carry their remote ID and a position. Projection version 2 is the active one for new events. |
artifact_access | Which tasks may download which stored artifact | Unique (task_id, digest). A digest download is allowed only through a task the caller can read. |
organization_freshness | One opaque token per organization for live-update signals | Contains no task content. |
Raw events that contain inline binary parts keep their complete original JSON in the artifact store; payload_json then holds the event with local download references plus the object key.
Commands, subscriptions and workers
| Table | Purpose | Notable columns and constraints |
|---|---|---|
task_commands | Durable intent to send or cancel | action (send, cancelTask), idempotency_key, message_id, payload_digest, payload_object_key, status (pending, dispatching, succeeded, failed, uncertain), result_json, last_error, skill_id. Unique (organization_id, idempotency_key). |
outbox_messages | Transactional outbox for background work | topic, aggregate_type, aggregate_id, status (pending, processing, processed, failed), attempts, available_at, lease_owner, lease_until, last_error. Written in the same transaction as the state that needs the side effect. |
task_subscriptions | Worker-owned stream observation per open task | status (pending, streaming, stopped), lease fields. Unique per task. |
task_push_registrations | Push-notification registration per task | status (pending, registering, active, deleting, deleted, failed), desired, lease fields, a per-registration rate window. Unique per task. No credentials are stored; tokens are derived from the signing key. |
sync_cursors | Polling reconciliation | resource_key empty for a ListTasks sweep or a local task ID for GetTask, page_token, status (pending, syncing, stopped, unsupported). Unique (organization_id, agent_id, tenant, resource_key). |
People, access and security
| Table | Purpose |
|---|---|
users | A person: display_name, enabled. |
external_identities | (issuer, subject) to user, unique. |
memberships | A user in an organization with role (admin, operator, viewer) and enabled. Unique (organization_id, user_id). |
user_sessions | Only the SHA-256 of the random opaque cookie value is stored (token_hash), with expires_at. Role and enabled state are read on every request, so revocation and demotion take effect immediately. |
login_attempts | Ten-minute login state, encrypted; consumed once. No provider token is stored. |
teams, team_memberships | Groups of people for grants. |
access_grants | subject_type (organization, membership, team), subject_id, agent_id, optional skill_id, permission (read, operate), enabled. |
security_rate_buckets | Fixed-window counters for rate limits; expire after 60 seconds. |
security_audit_events | Append-only audit facts: action, actor_type, actor_user_id (null for the system or an agent), target_id, unique event_key. |
Approvals
| Table | Purpose | Notable columns and constraints |
|---|---|---|
decision_requests | A request for a reviewer to approve one action on a task | status (pending, approved, rejected, changes_requested, expired, superseded), kind (send_message, send_data), risk, policy_json, requester_user_id (null when the agent asked), assigned_membership_id, current_revision, expires_at. Unique (organization_id, request_key). |
decision_revisions | Numbered, immutable versions of the proposed action | action_json, digest (SHA-256 of the canonical action), author_type (agent, user, system). Unique (request_id, number). |
decisions | One recorded outcome | outcome (approve, reject, edit, request_changes, delegate), rationale, revision_id, revision_digest, reviewer user and membership, optional delegate, idempotency_key (unique per organization). |
decision_executions | Correlates an approval with the command it sent and the outcome observed | command_id, message_id, status, observed_task_state. One per approving decision. |
Ownership and notifications
| Table | Purpose |
|---|---|
task_assignments | One row per task: assignee membership, claimed_at, due_at, escalation_level, escalated_at. Independent of the remote task state. |
task_assignment_events | Immutable history: assigned, claimed, released, due_set, due_cleared, escalated, with the actor (null for the system). |
task_notes | Immutable internal notes; unique (organization_id, note_key) makes retries idempotent. |
escalation_policies | Target membership for overdue owned tasks, per agent or for all agents (agent_id null). |
notifications | One immutable row per event, content-light (title, body, link). Its ID is the outbox message ID, so reprocessing cannot duplicate it. |
notification_recipients | One row per person told; only read_at can change. Unique (notification_id, membership_id). |
External webhook deliveries are outbox rows with topic notification.webhook; they are retried and end in a visible failed state.
Invariants the database enforces
Beyond foreign keys and unique constraints, PostgreSQL triggers (also present on PGlite) reject changes that would rewrite history:
| Table | Rule |
|---|---|
security_audit_events | No update or delete. |
decision_revisions, decisions | No update or delete. |
decision_requests | No delete. Identity, scope, title, summary, risk, policy, requester, expiry and creation time cannot change; status, assignment, revision number and bookkeeping can. |
decision_executions | No delete. Decision, revision, digest, command and message correlation cannot change; observed status can. |
task_assignment_events, task_notes, notifications | No update or delete. |
notification_recipients | No delete. Only the read mark can change. |
Application-level invariants (not database constraints): one live approval per task; a decision binds to an exact revision; a task's skill never changes after creation; stale writes are fenced with the
task's version and with lease owners on outbox, subscription, push and sync rows.
Because audit and decision rows cannot be deleted, removing a person's transcript content does not remove the minimal facts that explain a decision. There is no built-in retention or purge tool.
Outside the database
| Data | Where | Notes |
|---|---|---|
| Binary artifacts, inline event bytes, command payloads and original event archives | Filesystem artifact store at A2A_ARTIFACT_DATA_DIR | One file per (organization, SHA-256): <dir>/<organization id>/<sha256>. Content-addressed, written atomically, at most 16 MiB per object. Back it up with the database. |
| Vault keys | Environment (A2A_VAULT_KEYS) | Without the key that encrypted a credential, the credential is unrecoverable. |
| Launcher secrets | <data-dir>/secrets.json | Generated vault key for local mode. |
Projections and rebuild
The ledger (task_events) is the record of what the agent said; projections are derived and disposable. Reads use the active projection version, never the raw ledger. A rebuild replays the ledger with the
current projector into a new generation, then swaps it in under a lock. If the task changed meanwhile it retries; a missing or corrupt original archive, or an empty ledger, refuses to activate rather than
losing content. See npm run db:projections:rebuild and Durable tasks.
Migrations
One baseline migration, Migration0001_Baseline, creates this schema (schema version 1). It replaces the sixteen migrations of the development period; databases that applied them are adopted as they are (see
Upgrading and backups). Later changes are new migrations after it. List them with
npm run db:migration:list and check for drift with npm run db:schema:check (CLI). Down migrations exist for rollback in development; take a backup before relying on one.
Limits
- Retention, redaction of stored raw events and a data-export tool are not implemented. Raw events can contain sensitive agent content.
- Task titles, summaries and note bodies are stored as typed or received; apply your own policy about what agents and people put in them.
- The schema is internal. Do not read or write the tables directly from other software.
Further reading
Related
Created and maintained by Shashi Kanth G S at allsrc.dev. Visit shashikanth.me.
A2UI reference
For agent authors: which parts of the A2UI interface format Agent Taskbay renders, what it refuses, and the safety rules that hold whatever an agent sends.
Compatibility and known limitations
For anyone deciding whether Agent Taskbay fits their environment and agents: supported runtime versions, which A2A versions, transports and authentication schemes the gateway