# PostgreSQL service storage
This explicit PostgreSQL starter uses a service-owned `records` table and SQLx migrations. Use a separate database for each service: the migration ledger and table names are local to that database. Domain-specific tables and APIs belong to this project; do not put them in shared framework crates.
1. Provision PostgreSQL and a restricted runtime login with neither superuser, BYPASSRLS nor role-management privileges. Give a separate migration login schema ownership.
2. Set `MIGRATION_DATABASE_URL` and run `make migrate` from this service. Grant the runtime role SELECT/INSERT/UPDATE/DELETE on `records` through provisioning.
3. Set `DATABASE_URL`, trusted `TENANT_ID`, stable `STORAGE_ENCRYPTION_NAMESPACE`, `ENCRYPTION_KEY_HEX` and `ENCRYPTION_KEY_ID`, then run `make run`. Remote PostgreSQL requires verified TLS. `DATABASE_ALLOW_PLAINTEXT_DEVELOPMENT=true` explicitly permits plaintext development networking, including local Docker service names.
Keep the storage namespace stable across service/package renames; changing it requires deliberate ciphertext migration. The framework validates connections and transaction-local tenant context. SQL includes explicit tenant predicates and forced row-level security. `PostgresRepository` encrypts names before SQL; it returns protected members, never implicit plaintext. Use the framework's acknowledged audit disclosure methods after application authorization when adding a read API. `persistence/entities/stored_record.rs` is the actual ciphertext-only model used on SQL writes and hydrated reads. Its persisted compliance derive requires protected fields and generates fixed member/purpose bindings; replacing the protected value with plaintext fails compilation. `persistence/compliance_schema.rs` compiles that exact entity source to produce the build output `.forklaunch/compliance.json`, rather than maintaining a shadow shape. Binding validation does not authenticate ciphertext or authorize disclosure: verified tenant/resource context, key lookup and acknowledged audit remain explicit runtime requirements. Configure `ENCRYPTION_PREVIOUS_KEYS_JSON` for reads during key rotation; retaining old keys does not rotate existing rows.
HTTP health queries this repository; unavailable storage returns a failure instead of reporting zero records. Startup never runs migrations implicitly. The scaffold does not create domain CRUD HTTP endpoints, assign production roles, or grant privileges automatically.