agent-taskbay
Reference

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

RuleDetail
Local IDsEvery record has a UUID generated by Agent Taskbay before it is stored. URLs and APIs use these local IDs.
Tenancyorganization_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 messagesA 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 IDsOwnership, notes and approvals are keyed by local task IDs. They are independent of A2A contextId and referenceTaskIds.
External identitiesA 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

TablePurposeNotable columns and constraints
organizationsTenancy rootslug 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.
agentsAn agent registered by Agent Card URLcard_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_snapshotsHistory of discovered cardsraw_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_credentialsEncrypted credential binding for one agentkind, 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

TablePurposeNotable columns and constraints
tasksOne 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_eventsAppend-only ledger of everything observedsequence (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_projectionsNormalized views rebuilt from the ledgerKeyed 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_accessWhich tasks may download which stored artifactUnique (task_id, digest). A digest download is allowed only through a task the caller can read.
organization_freshnessOne opaque token per organization for live-update signalsContains 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

TablePurposeNotable columns and constraints
task_commandsDurable intent to send or cancelaction (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_messagesTransactional outbox for background worktopic, 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_subscriptionsWorker-owned stream observation per open taskstatus (pending, streaming, stopped), lease fields. Unique per task.
task_push_registrationsPush-notification registration per taskstatus (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_cursorsPolling reconciliationresource_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

TablePurpose
usersA person: display_name, enabled.
external_identities(issuer, subject) to user, unique.
membershipsA user in an organization with role (admin, operator, viewer) and enabled. Unique (organization_id, user_id).
user_sessionsOnly 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_attemptsTen-minute login state, encrypted; consumed once. No provider token is stored.
teams, team_membershipsGroups of people for grants.
access_grantssubject_type (organization, membership, team), subject_id, agent_id, optional skill_id, permission (read, operate), enabled.
security_rate_bucketsFixed-window counters for rate limits; expire after 60 seconds.
security_audit_eventsAppend-only audit facts: action, actor_type, actor_user_id (null for the system or an agent), target_id, unique event_key.

Approvals

TablePurposeNotable columns and constraints
decision_requestsA request for a reviewer to approve one action on a taskstatus (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_revisionsNumbered, immutable versions of the proposed actionaction_json, digest (SHA-256 of the canonical action), author_type (agent, user, system). Unique (request_id, number).
decisionsOne recorded outcomeoutcome (approve, reject, edit, request_changes, delegate), rationale, revision_id, revision_digest, reviewer user and membership, optional delegate, idempotency_key (unique per organization).
decision_executionsCorrelates an approval with the command it sent and the outcome observedcommand_id, message_id, status, observed_task_state. One per approving decision.

Ownership and notifications

TablePurpose
task_assignmentsOne row per task: assignee membership, claimed_at, due_at, escalation_level, escalated_at. Independent of the remote task state.
task_assignment_eventsImmutable history: assigned, claimed, released, due_set, due_cleared, escalated, with the actor (null for the system).
task_notesImmutable internal notes; unique (organization_id, note_key) makes retries idempotent.
escalation_policiesTarget membership for overdue owned tasks, per agent or for all agents (agent_id null).
notificationsOne immutable row per event, content-light (title, body, link). Its ID is the outbox message ID, so reprocessing cannot duplicate it.
notification_recipientsOne 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:

TableRule
security_audit_eventsNo update or delete.
decision_revisions, decisionsNo update or delete.
decision_requestsNo delete. Identity, scope, title, summary, risk, policy, requester, expiry and creation time cannot change; status, assignment, revision number and bookkeeping can.
decision_executionsNo delete. Decision, revision, digest, command and message correlation cannot change; observed status can.
task_assignment_events, task_notes, notificationsNo update or delete.
notification_recipientsNo 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

DataWhereNotes
Binary artifacts, inline event bytes, command payloads and original event archivesFilesystem artifact store at A2A_ARTIFACT_DATA_DIROne 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 keysEnvironment (A2A_VAULT_KEYS)Without the key that encrypted a credential, the credential is unrecoverable.
Launcher secrets<data-dir>/secrets.jsonGenerated 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

Created and maintained by Shashi Kanth G S at allsrc.dev. Visit shashikanth.me.

On this page