Managed Databases
Enable any app to get a one-click managed Postgres database with automatic environment injection. No manual database setup, no credential management.
Status
Databases are in preview: the REST API and MCP surface are final, but provisioning returns placeholder connection values (host stub-shared-pg-01.local) instead of a live Postgres instance. When managed provisioning goes live, the same calls return real connections. Code written against the injected env vars keeps working unchanged.
Overview
Managed databases are stored in data/app-infra/databases.json with append-only semantics. The password is returned exactly once from the provision call and never stored at rest. The MCP tool automatically publishes DATABASE_URL into the app’s environment via the App Secrets producer API, so the password never reaches the caller.
End-to-End Flow
- Operator calls
add_app_database(app_id='app-abc123', engine='postgres')via MCP. - The MCP tool verifies app ownership, then POSTs to the platform REST API
/api/app-infra/databaseswith the app details. - The platform provisions the database (in preview: a placeholder connection; once live: a real database on a shared Postgres host) and returns
db_idplus password once. - The MCP tool immediately publishes
DATABASE_URLinto the app’s env via the producer API (Bearer token); the caller never sees the password. - The MCP tool appends
db_idtoApp.db_refsand returns{db_id, status, env_keys, env_published, session_token, next}. - On next
deploy_appcall, the app VM receivesDATABASE_URLin/etc/openfactory/app.envautomatically; the app connects without any manual credential setup.
MCP Tools
| Tool | Purpose | Example Call |
|---|---|---|
add_app_database | Provision a managed Postgres database for an app and automatically publish | add_app_database(app_id=‘app-12345678’, engine=‘postgres’) |
list_app_databases | List all databases provisioned for an app (metadata only, no passwords). | list_app_databases(app_id=‘app-12345678’) |
add_app_database
Provision a managed Postgres database for an app and automatically publish DATABASE_URL into the app’s environment.
Example Call:
add_app_database(app_id='app-12345678', engine='postgres')Example Response (when env_published=true):
{
"db_id": "db-a1b2c3d4e5f6",
"status": "stub",
"engine": "postgres",
"env_keys": ["DATABASE_URL"],
"env_published": true,
"session_token": "user@example.com",
"next": "deploy_app(app_id='app-12345678', vm_name=...). DATABASE_URL is already in the app's env and will be injected on the next deploy."
}Example Response (when env_published=false):
{
"db_id": "db-a1b2c3d4e5f6",
"status": "stub",
"engine": "postgres",
"env_keys": [],
"env_published": false,
"connection_url": "postgres://app_myapp:password123@stub-shared-pg-01.local:5432/app_myapp",
"publish_skipped_reason": "OPENFACTORY_DB_PRODUCER_TOKEN is not configured",
"session_token": "user@example.com",
"next": "set_app_env(app_id='app-12345678', set={'DATABASE_URL': <connection_url>}) then deploy_app(...)"
}list_app_databases
List all databases provisioned for an app (metadata only, no passwords).
Example Call:
list_app_databases(app_id='app-12345678')Example Response:
{
"databases": [
{
"db_id": "db-a1b2c3d4e5f6",
"app_id": "app-12345678",
"engine": "postgres",
"shape": "shared-tenant",
"shared_host": "stub-shared-pg-01.local",
"database": "app_myapp",
"username": "app_myapp",
"host": "stub-shared-pg-01.local",
"port": 5432,
"status": "stub",
"notes": "STUB MODE: no real Postgres provisioned.",
"created_at": "2026-06-26T01:52:00Z"
}
],
"session_token": "user@example.com"
}MCP Response Fields:
session_token: Appended by the MCP tool wrapper (not present in raw REST responses)- All other fields come directly from the REST API
REST API
| Method | Path | Purpose | Auth |
|---|---|---|---|
| POST | /api/app-infra/databases | Provision a new managed Postgres database for an app. Returns the connection details (host, port, database, username, password, url) exactly once; password never stored. | JWT (authenticated user; requests are scoped to apps you own) |
| GET | /api/app-infra/databases/{db_id} | Fetch metadata for a specific database (no password). Returns database configuration, host, port, status, and creation timestamp. | JWT (authenticated user) |
| GET | /api/app-infra/databases?app_id={app_id} | List all databases for an app with optional filtering by app_id. Returns metadata only (no passwords). | JWT (authenticated user) |
Provision Endpoint
Request (POST /api/app-infra/databases):
{
"app_id": "app-12345678",
"owner_user_id": "user@example.com",
"slug": "my-app",
"engine": "postgres"
}Response (201):
{
"db_id": "db-a1b2c3d4e5f6",
"status": "stub",
"connection": {
"host": "stub-shared-pg-01.local",
"port": 5432,
"database": "app_myapp",
"username": "app_myapp",
"password": "actual_password_here",
"url": "postgres://app_myapp:actual_password_here@stub-shared-pg-01.local:5432/app_myapp"
}
}The password is returned exactly once and must be published to the app’s environment immediately. Subsequent GET requests never return the password.
Get Database Metadata
Response (GET /api/app-infra/databases/{db_id}):
{
"db_id": "db-a1b2c3d4e5f6",
"app_id": "app-12345678",
"engine": "postgres",
"shape": "shared-tenant",
"shared_host": "stub-shared-pg-01.local",
"database": "app_myapp",
"username": "app_myapp",
"host": "stub-shared-pg-01.local",
"port": 5432,
"status": "stub",
"notes": "STUB MODE: no real Postgres provisioned.",
"created_at": "2026-06-26T01:52:00Z"
}List Databases
Response (GET /api/app-infra/databases?app_id=app-12345678):
{
"databases": [
{
"db_id": "db-a1b2c3d4e5f6",
"app_id": "app-12345678",
"engine": "postgres",
"shape": "shared-tenant",
"shared_host": "stub-shared-pg-01.local",
"database": "app_myapp",
"username": "app_myapp",
"host": "stub-shared-pg-01.local",
"port": 5432,
"status": "stub",
"notes": "STUB MODE: no real Postgres provisioned.",
"created_at": "2026-06-26T01:52:00Z"
}
]
}