Databases & files
Choose the store based on access patterns and recovery requirements. There is no need to use every datastore in one app.
Choose a store
| Data | Default home | Why |
|---|---|---|
| Application documents, relationships, accounting | RDS PostgreSQL | Transactions, joins, flexible queries, shared migrations |
| Owner-scoped settings with predictable key access | Optional DynamoDB | Explicit keys, conditional updates, managed scale |
| Audio, video, images, large exports | S3 | Direct uploads, multipart transfer, signed downloads |
| Temporary files and local generation output | kit.data_dir |
Convenient local workspace; requires care when scaling |
Arena and the shared platform already use RDS. DynamoDB is an additional abstraction, not a replacement for the SQL store.
PostgreSQL documents
store = kit.store.for_owner(user.id)
record_id = await store.put("notes", {"title": "First note"})
record = await store.get("notes", record_id)
recent = await store.list("notes", limit=20, offset=0)
await store.delete("notes", record_id)
put is an insert-or-replace operation. list supports bounded pagination and top-level JSON filters with where. Its response includes record IDs. For relational constraints, joins, or multi-record transactions, use explicit SQLAlchemy tables and reviewed migrations.
Custom table names should start with the app slug. Production runtime roles have data permissions, not schema-creation privileges.
Optional DynamoDB
Provision a table for the app, declare kv in its manifest, and bind verified identity:
from at.kv import Conflict
kv = kit.kv.for_owner(user.id)
created = await kv.put("preferences", "theme", {"color": "blue"})
updated = await kv.put(
"preferences", "theme", {"color": "green"},
expected_version=created["version"],
)
page = await kv.query("preferences", prefix="th", limit=20)
Without expected_version, writes are create-only. A stale version raises Conflict. Reads are strongly consistent. Prefix queries return a signed cursor; follow it until absent, including after an empty page.
Values are JSON objects capped at 300 KiB. Large payloads belong in S3. Optional expiry is enforced by the adapter before DynamoDB's asynchronous TTL cleanup. There are no scans, arbitrary SQL-style filters, or silent fallback to PostgreSQL.
The Infrastructure Demo is a working reference. DynamoDB operator notes cover provisioning, throughput limits, and point-in-time recovery.
S3 upload workflow
Call kit.enable_files() to expose the shared file API. Endpoints are relative to the app:
| Endpoint | Purpose |
|---|---|
POST /api/files/uploads |
Reserve an upload with filename, size, and content type |
POST /api/files/{id}/parts/{part} |
Obtain a signed URL for an upload part |
GET /api/files/{id}/parts |
Inspect uploaded parts for resume |
POST /api/files/{id}/complete |
Validate and finalize the object |
GET /api/files |
List the authenticated owner's catalog |
GET /api/files/{id}/url |
Get a short-lived download URL |
DELETE /api/files/{id} |
Cancel an upload or delete an owned file |
The browser sends file bytes directly to S3. The platform owns metadata, authorization, and quota reservations. Pending uploads count toward quota so parallel reservations cannot bypass it. Default owner limits are 100 GiB and 1,000 files per app; configuration can change them.
The Media Library demonstrates the complete workflow. Arena has a separate artifact API and implementation; do not assume endpoint shapes are interchangeable.
Recovery and cleanup
The source media bucket is versioned. New media versions replicate to a separate private recovery bucket, and source deletion does not propagate there. Application credentials cannot delete recovery copies.
This is same-account, same-region recovery, not an immutable cross-account vault. Recovery current versions have no automatic expiry; noncurrent recovery versions retain 90 days. Storage costs grow with media and retained versions.
Hourly maintenance reconciles uploads older than two days: it recovers completed S3 objects whose catalog update failed, or releases unused reservations. It does not expire completed user files. SQL catalog data is protected separately by nightly backups and RDS recovery.