Managed databases for agents

This reference extends https://app.tokay.io/llms.txt with managed database operations for AI agents. Use these operations only when the user explicitly asks or a named readiness action requires them. Never rotate credentials, restore a snapshot, or replace data without user approval.

Last updated

AI agent? Start with llms.txt.

The resource and connection model

  • A project has at most one managed instance per engine (Postgres, MySQL, MongoDB, Redis), shared by its services. A resource is one physical engine, not one database.
  • Postgres, MySQL, and MongoDB resources contain logical databases Tokay knows about, such as app and auth. Each logical database has its own generated application username and versioned password. Changing only the database name in a connection string does not grant access to another logical database.
  • One serviceResourceConnection groups all env vars for one application connection and binds them to one logical database. An app that needs two databases receives two independent sets of env vars.
  • Redis credentials stay scoped to the whole resource because its numbered databases are not authorization boundaries.
  • Admin and read only platform credentials are never delivered to apps. The query endpoint uses the read only principal, while app tasks and migrations receive the service's ordinary scoped connection.
  • The query endpoint is read only. Data writes belong in a service task the user requested, and schema changes belong in release migrations.
  • A MySQL app that uses one login for SQL across schemas must split that access into separate connections. Static evidence blocks deploy with MYSQL_CROSS_DATABASE_CONNECTION_UNSUPPORTED, and MySQL 1044/1142 failures that correlate with that topology receive the same diagnosis.

Tokay still creates infrastructure automatically. Confirming a connection records the choice, but the engine is physically provisioned only during deploy.

What to call

To Use
List resources and logical databases project.projectResources
Run a read only query POST /resources/:id/query
Export data POST /resources/:id/export_ticket, then GET the path
Stage an import upload POST /projects/:id/resource_import_blobs
Import while binding a connection setServiceResourceConnectionLogicalDatabaseWithImport
Import into an existing logical database createProjectResourceImport
Poll, confirm, cancel, or retarget an import projectResourceImport, confirmProjectResourceImport, cancelProjectResourceImport, retargetProjectResourceImport
Rotate an application password rotateProjectResourceLogicalDatabasePassword
Undo or redo a rotation rollbackProjectResourceLogicalDatabasePassword / redoProjectResourceLogicalDatabasePassword
List and restore snapshots projectResource.projectResourceSnapshots, restoreProjectResourceSnapshot

Postgres extensions

Tokay's managed Postgres runs PostgreSQL 18.3. Normal migrations can install the extensions Postgres marks trusted, including common options such as pg_trgm, hstore, uuid-ossp, pgcrypto, ltree, and citext. Tokay also supplies and permits vector from pgvector 0.8.5 and postgis from PostGIS 3.6.4.

Extensions belong to one logical database. Add ordinary SQL to the migration that runs against the intended database:

create extension if not exists "vector";

Use the extension's exact name. The default target schema is public. Run the migration through the service's confirmed connection for that logical database so the extension installs where the app uses it. The resource query endpoint can inspect pg_catalog.pg_extension, but it is read only and is never an installation path. Other extensions that require superuser authority remain blocked.

Find resources and logical databases

query Resources($projectId: ID!) {
  project(id: $projectId) {
    projectResources(first: 10) {
      nodes {
        id
        resourceType
        runtimeName
        logicalDatabases(first: 100, orderBy: [NAME_ASC]) {
          nodes {
            id
            name
            runtimeUsername
            passwordVersions(first: 20, orderBy: [VERSION_DESC]) {
              nodes {
                id
                version
                isCurrent
                reconciledAt
                failedAt
                failureMessage
                createdAt
              }
            }
          }
        }
      }
    }
  }
}

GraphQL IDs are Relay node IDs. Use projectResource.id in REST resource paths, logicalDatabases.nodes[].id for password actions and imports that target an existing logical database, and a setup blocker's serviceResourceConnection.id when binding an application connection.

Query

POST {API_ORIGIN}/resources/:resourceId/query
Content-Type: application/json

{ "query": "select count(*)::int as n from tasks", "database": "app" }

query is a SQL string for Postgres and MySQL, a Redis command array such as ["GET", "mykey"], or a MongoDB command or find object. database is optional for Postgres, MySQL, and MongoDB and must name a logical database Tokay knows about. Omitting it targets the contract default. Redis rejects database targeting.

The response streams one JSON object per line. Engine failures still return HTTP 200 with an error row, {"_tokay_error":"QUERY_FAILED","message":"..."}, so inspect the first line. Envelope and target errors return HTTP 400 in the same NDJSON shape. Writes fail because the endpoint uses a read only transaction or principal.

Export

POST {API_ORIGIN}/resources/:resourceId/export_ticket
Content-Type: application/json

{ "format": "native", "database": "app" }

Both keys are optional. database scopes Postgres, MySQL, or MongoDB to one logical database Tokay knows about. Omitting it exports all known logical databases. Exports authenticate with each selected logical database's own credential, not a root secret. Redis rejects database targeting.

native preserves the engine backup shape and is the format imports accept back. CSV is supported for Postgres, MySQL, and MongoDB and returns a ZIP with one CSV per table or collection. Redis returns EXPORT_FORMAT_UNSUPPORTED_FOR_ENGINE.

The response is {"downloadPath":"/resource_exports/<ticket>"}. Fetch that path once without an Authorization header. The ticket expires quickly and works once.

Import an existing database

Imports load an existing dump into one logical database per operation:

  • Postgres plain SQL, pg_dump custom format, or a Tokay native ZIP containing one database.
  • MySQL logical SQL or a Tokay native ZIP containing one database.
  • MongoDB mongodump --archive, compressed or uncompressed.
  • Redis is unsupported.

Dumps that span a whole cluster, define global roles, or contain several databases are rejected. Uploaded bytes never run through a managed admin connection. Tokay restores them in an isolated sandbox that runs the same engine and holds no secrets, normalizes the result, and proves a fresh restore under the application role before touching the managed engine.

Import step 1: stage the raw bytes

POST {API_ORIGIN}/projects/:projectId/resource_import_blobs
Authorization: Bearer <token>
Content-Type: application/octet-stream

<raw dump bytes>

The project path uses a Relay node ID. The HTTP 202 response contains stagedImportHandle, sha256, sizeBytes, and expiresAt. Staging only holds the bytes. It is not yet an import, and an unused blob expires after one hour.

Import step 2a: bind during onboarding

Resolve the normal UNCONFIRMED_LOGICAL_DATABASE blocker and attach the handle in the same mutation:

mutation BindDatabaseWithImport(
  $connection: ID!
  $database: String!
  $handle: String!
) {
  setServiceResourceConnectionLogicalDatabaseWithImport(input: {
    connection: $connection
    logicalDatabaseName: $database
    stagedImportHandle: $handle
  }) {
    projectResourceImport {
      id
      importState
      logicalDatabase { id name }
    }
  }
}

This transaction makes the connection and the import reference the same logical database. No later confirmation can send the app to a different one.

Import step 2b: target an existing logical database

mutation ImportExisting($database: ID!, $handle: String!) {
  createProjectResourceImport(input: {
    logicalDatabase: $database
    stagedImportHandle: $handle
  }) {
    projectResourceImport { id importState }
  }
}

Import step 3: poll the durable import

query DatabaseImport($id: ID!) {
  projectResourceImport(id: $id) {
    id
    importState
    importStep
    sourceFormat
    sourceSizeBytes
    normalizedFormat
    normalizedSizeBytes
    expandedSizeBytes
    errorCode
    errorMessage
    logicalDatabase { id name }
    projectResource { id resourceType runtimeName }
  }
}
State Action
PENDING_VALIDATION, VALIDATING Poll. Tokay is validating and normalizing without changing active data.
AWAITING_DEPLOY Resume ordinary deploy readiness. Restore runs before release actions and app startup.
AWAITING_CONFIRMATION Existing data will be replaced. Explain the target and downtime, obtain explicit user approval, then confirm.
EXECUTING Poll. Tokay owns the maintenance window, rollback, and app recovery.
SUCCEEDED Done.
FAILED Stop and surface errorCode plus errorMessage. A corrected dump needs a new upload.
CANCELED, EXPIRED Stop. A new import needs a new upload.

Confirm only with explicit destructive approval:

mutation ConfirmImport($id: ID!) {
  confirmProjectResourceImport(input: { projectResourceImport: $id }) {
    projectResourceImport { id importState importStep }
  }
}

cancelProjectResourceImport uses the same projectResourceImport input and is valid only before destructive execution. retargetProjectResourceImport takes projectResourceImport plus a destination logicalDatabase. Earlier normalization work may be reused when the new target runs the same engine, but target and capacity checks run again.

Validation enforces independent limits on upload size, expanded data, runtime, and disk capacity. Replacement blocks other maintenance on the resource while it runs, but stops only the services credentialed for the target logical database. The target keeps its stable name. Rollback protection lasts only until the old or new application is healthy again, and Tokay does not offer a delayed rollback that discards new writes.

Rotate and restore application passwords

Postgres, MySQL, and MongoDB application passwords belong to logical databases. Rotate only on user request or known exposure:

mutation RotateDatabasePassword($database: ID!) {
  rotateProjectResourceLogicalDatabasePassword(input: {
    logicalDatabase: $database
  }) {
    logicalDatabasePassword {
      id
      version
      isCurrent
      reconciledAt
      failedAt
      failureMessage
    }
  }
}

Rotation updates the engine first, then recreates or rechecks only the consumers of that logical database. reconciledAt means both sides converged. If app recovery fails after engine rotation, Tokay automatically restores the latest previously reconciled version.

rollbackProjectResourceLogicalDatabasePassword and redoProjectResourceLogicalDatabasePassword both take { logicalDatabase: <id> }. To restore a particular historical row, call restoreProjectResourceLogicalDatabasePassword with { password: <password version id> }. Plaintext is never readable from the API. Redis password rotation continues through the resource output mutations because Redis credentials stay scoped to the whole resource.

Snapshots

query Snapshots($id: ID!) {
  projectResource(id: $id) {
    projectResourceSnapshots(first: 10, orderBy: [CREATED_AT_DESC]) {
      nodes { id createdAt snapshotReason storageBytes storageRemovedAt }
    }
  }
}

Release snapshots cover the managed resource. Import replacement uses its own rollback protection scoped to the target logical database during the maintenance window. Snapshot restore is a destructive user decision. Explain what current data will be lost and obtain confirmation before calling restoreProjectResourceSnapshot.

Error shapes

You see It means Do this
RESOURCE_QUERY_DATABASE_NOT_FOUND / EXPORT_DATABASE_NOT_FOUND database names an unknown logical database List logicalDatabases, use an exact name
RESOURCE_QUERY_DATABASE_TARGET_UNSUPPORTED / EXPORT_DATABASE_TARGET_UNSUPPORTED database was sent for Redis Remove the target
PROJECT_RESOURCE_MAINTENANCE_ACTIVE An import replacement is running on that resource Poll the import, hold other maintenance until it finishes
DATABASE_IMPORT_UNSUPPORTED_ENGINE Redis import requested Stop. Redis import is not supported
DATABASE_IMPORT_*LIMIT* or HTTP 413 An upload or expanded data limit was exceeded Produce a smaller supported logical dump
DATABASE_IMPORT_SCHEMA_REMAP_UNSAFE A MySQL stored program uses the source database name ambiguously Import into that database name, or remove or rename the qualified reference or alias
DATABASE_IMPORT_FORMAT_UNSUPPORTED, DATABASE_IMPORT_ARCHIVE_*, or DATABASE_IMPORT_RESTORE_FAILED The restore sandbox rejected the input Show errorMessage, create a corrected dump
MYSQL_CROSS_DATABASE_CONNECTION_UNSUPPORTED One MySQL connection crosses schemas Split the code or config into separate connection env vars
HTTP 200 row with _tokay_error: QUERY_FAILED The engine rejected a read only query Fix the statement or the database target
404 on an already fetched downloadPath Export tickets work once Mint a new ticket