RFC dan proposal mencatat rancangan. Untuk kemampuan yang benar-benar dapat dipakai, lihat matriks kapabilitas runtime dan catatan rilis.
RFC 0004: Executable Layered APIs and PostgreSQL
Status: Accepted Areas: Appendix G.5–G.18; Appendix H.8, H.10, H.12 Depends on: RFCs 0001, 0002, and 0003
Decision: Accepted by the project owner on 2026-10-23. This approves the design direction; implementation and conformance evidence remain outstanding.
2026-10-27 scoped-cleanup update: The compiler has explicit defer direct_call(...) for lexical exits, and std/pg/transaction.yz provides a transaction guard that attempts rollback unless explicitly committed or rolled back. Failure or an uncertain commit closes the connection and does not retry. An implicit rollback failure or timeout attempts to emit a fixed structured cleanup-failure event with no SQL, credential, or server error text. The migration runner now uses the guard, so newly added early returns and ? error propagation trigger cleanup. Language/API and strict PostgreSQL protocol-peer tests pass normally and with ASan/UBSan; the scoped smoke also verifies LIFO pool release on commit, rollback failure, and timeout. This protocol fixture is not a live PostgreSQL server; the scoped guard and migration failure path still need live-server verification. Concurrent connection use, ownership-enforced pool checkout, and automatic disposal remain incomplete.
2026-10-27 pool cleanup update: pg_pool_release(handle, true) checks that the socket handle is still active and discards it if transaction cleanup already closed it. The C pool harness now closes a leased socket before release and verifies the handle is removed even when the caller reports it as healthy; normal and ASan/UBSan runs pass. tests/pg-transaction-scope-smoke.sh now exercises pooled transaction cleanup too: commit returns a live lease to idle, while rollback failure and timeout close and discard it after the transaction guard runs. For pooled transactions, register pool release before the transaction guard so LIFO cleanup rolls back first. This ordering remains a documented caller convention, not an ownership/type-system guarantee.
Current implementation status (2026-10-27): Partial. Product repository functions use the YAZA PostgreSQL adapter for all six CRUD operations. Migration and API CRUD/restart persistence previously passed against PostgreSQL 17.6; after the lifecycle-hook change, migration plus the full database-configured HTTP smoke passed against disposable PostgreSQL 18.6, including startup seeding, readiness, CRUD, restart persistence, and readiness failure/recovery after pausing/resuming the database. The disposable PostgreSQL smoke injects SQLSTATE 23505, 23514, and 57014 through trigger fixtures and verifies safe 409/504 responses; it also terminates PostgreSQL during an active create and verifies 503 unavailable, then restarts PostgreSQL and verifies /readyz recovers in the same API process. Product list pagination passes typed limit and offset through controller→service→repository; in-memory and PostgreSQL paths reject invalid bounds as 422 InvalidInput. Service and repository bindings receive compile-time signature/call-chain checks but remain static, not runtime polymorphism. Product create/replace/patch/find now return typed Result[ProductRecord, ApiError] through all three layers; list returns Result[List[ProductRecord], ApiError] as a typed JSON array; delete returns a typed ProductDeleteOutcome behind its bodyless 204 response. API startup/schema/readiness use pure-YAZA PostgreSQL hooks, Product operations use a bounded process-local pool, and generated API binaries do not link libpq. Without DATABASE_URL, ProductRepository calls std/api/memory_records.yz; its bounded native adapter mutates the in-memory table and returns Result values through the ordinary handler response path. Starter CRUD and the complete no-database API HTTP fixture pass, including chunked JSON create. The adapter is not general YAZA-owned persistence. Remote TLS, broader-version coverage, concurrent-handler evidence, and automated CI remain open. The interrupted database create returns 503 and is not retried automatically. See the runtime matrix, Appendix G, and API completion roadmap for current evidence.
2026-10-28 route-group and middleware update: Route files support nested, indentation-scoped groups with normalized static prefixes, inherited auth required|auth public policies, and inherited rate_limit REQUESTS per WINDOW_MS policies. A route-level policy overrides its group; group-level rate buckets are shared by that group's nested routes and isolated from other scopes. The compiler expands paths and policies before validation, runtime generation, OpenAPI, and Postman. OpenAPI records effective limits with x-yaza-rateLimit. HTTP smoke proves inherited auth and rate limiting, override behavior, the 429 envelope and Retry-After, a separate unaffected group, and OpenAPI/Postman output. Generation rejects traversal prefixes. The scoped bucket C harness passes normal and ASan/UBSan builds; macOS bootstrap passes three self-hosted stages with byte-identical final compiler C and 42/42 language tests. This is a bounded built-in rate-limit policy, not a general middleware chain or distributed quota system.
2026-10-28 route-scoped CORS update: Route and group declarations accept an exact cors "https://origin" policy. Nested groups inherit the policy, a route may override it, and that route origin takes precedence over the server-wide YAZA_CORS_ORIGIN. The runtime applies the selected policy to actual responses and CORS preflight, while preserving common and controller-declared request-header checks. OpenAPI records the effective origin as x-yaza-corsOrigin; runtime startup reports the number of route-scoped policies. This adds built-in CORS composition to route metadata but does not provide a general middleware chain, multiple origins per policy, or arbitrary user middleware.
2026-10-28 exact Host policy update: The runtime validates each HTTP Host against YAZA_ALLOWED_HOSTS, a comma-separated list of at most 32 exact ASCII host names with no wildcard or configured port. Matching is case-insensitive and ignores the request port. Loopback binds default to 127.0.0.1, localhost, and the configured loopback address; non-loopback binds require the allowlist explicitly. The API HTTP smoke rejects wildcard configuration and an unlisted Host and accepts a configured mixed-case host on macOS. Linux and browser DNS-rebinding behavior remain unverified. This is an additional routing boundary, not authentication or TLS.
2026-10-28 route-scoped JWT verification update: Route and group declarations now accept auth jwt. The initial access-token profile accepts compact JWS with HS256 only, requires typ: JWT, strict unpadded canonical base64url, configured YAZA_JWT_SECRET (32–4096 bytes), exact YAZA_JWT_ISSUER and YAZA_JWT_AUDIENCE, and an integer exp claim; optional integer nbf and iat claims are checked. Signature verification precedes claims parsing; signature comparison is fixed-width, and a verified expired token returns 401 token_expired. Unsupported JOSE header fields and duplicate JSON keys fail closed. OpenAPI adds x-yaza-authentication: jwt-hs256 and bearer security; Postman emits a bearer token field. JWT-only non-loopback binding requires at least one JWT-protected route and explicit auth policies on every declared route. The route middleware smoke covers accepted/rejected tokens, missing configuration, generator output, and a server using JWT without YAZA_API_TOKEN; the macOS bootstrap passed the three-stage self-host build and all 42 language tests. Token issuance, typed principals, centralized roles/ownership authorization, revocation/key rotation, asymmetric/JWKS/OIDC, TLS, and full middleware composition remain incomplete. This is a bounded verifier profile, not a complete identity system.
2026-10-28 verified JWT claim access update: A successfully authenticated JWT payload is now carried only through the matching handler's request context. http_auth_claim_text(name) -> Option[Text] decodes top-level string claims, returns none for absent/non-string claims and for non-JWT routes, caps decoded text at 1023 UTF-8 bytes, and the request-scoped payload copy is wiped after dispatch. A YAZA HTTP smoke verifies subject/scope access, escaped Unicode, absent subject behavior, malformed-subject rejection, and rejection of spoofed identity headers on public/shared-Bearer routes. Token issuance, a typed principal model, centralized role/permission decisions, ownership rules, and sessions remain incomplete; applications must still enforce their own authorization policy after reading trusted claims.
2026-10-28 JWT audience-array update: JWT aud now accepts either a string or a non-empty array of strings, and succeeds only when the configured audience occurs in the claim. Empty, mixed-type, malformed, and non-matching arrays are rejected. Normal and ASan/UBSan route middleware smokes cover matching and rejected array forms. This preserves bounded verification: the compact token and decoded payload sizes remain capped.
2026-10-27 typed Product write update: Product create, replace, and patch now return Result[ProductRecord, ApiError] through controller, service, repository interface, and implementation. The repository runs bounded json_shape(Text) decoding before returning each record; JSON response fields remain unchanged. OpenAPI derives ProductRecord schemas for GET, POST, PUT, and PATCH. Generation and HTTP smoke verify schema and actual create/replace/patch response bodies; the full macOS bootstrap passes three self-hosted stages, 40/40 language tests, API layer/generation checks, and normal plus ASan/UBSan HTTP smokes. Delete is typed but its 204 response is bodyless. 2026-10-27 typed Product collection update: Product list now returns Result[List[ProductRecord], ApiError] through controller, service, repository interface, and implementation. Bounded json_shape_list(Text) decoding validates each record; the HTTP runtime serializes the typed value as a JSON array, and OpenAPI derives array items from ProductRecord. HTTP, layer-conformance, generation, and three-stage bootstrap evidence cover the slice. Runtime-polymorphic repository dispatch and generalized typed collection codecs remain incomplete. The API dispatcher also accepts direct List[Shape] and Option[List[Shape]] handler results; present values serialize as arrays, none maps to 404, and OpenAPI/Postman describe the array item schema. Full API HTTP smoke passes normally and under ASan/UBSan; generation smoke passes.
2026-10-27 primitive API array update: API request shapes decode primitive List[Number], List[Int32], List[Float64], List[Boolean], and List[Text], along with nullable Option[List[T]]. The runtime validates item kinds, signed integer bounds, finite Float64 values, and min_items/max_items; shape response serialization and OpenAPI/Postman generation preserve array item types. The api-complete fixture exercises round trips, null/omitted optionals, invalid element types, overflow, bounds, generated examples, and absolute URLs. Normal and ASan/UBSan API HTTP smokes, OpenAPI/Postman/Swagger smoke, 42/42 language tests, and the three-stage macOS bootstrap pass. General collection-valued handler outcomes and arbitrary collection codecs remain incomplete.
2026-10-28 scalar-list handler response update: API handlers can now return List[Number], List[Int32], List[Float64], List[Boolean], and List[Text], including present or missing Option[List[T]] and successful Result[List[T], E] values. The runtime checks the declared element type and list bounds before JSON serialization; OpenAPI derives each array item schema from the handler return type. The fixture covers all five scalar types and optional/result routes; normal and ASan/UBSan HTTP smokes and OpenAPI/Postman/Swagger generation pass. The three-stage macOS self-host bootstrap and all 43 language tests pass. Nested arbitrary collections and other collection element types remain outside this implementation slice.
2026-10-27 typed request-array update: http_body_shape now decodes List[DeclaredShape] and nullable Option[List[DeclaredShape]] fields from JSON arrays. It rejects non-object items and invalid nested shape fields, applies nested validation and declared min_items/max_items bounds, and caps input arrays at 64 objects. OpenAPI derives nested item schemas, nullable optional fields, and declared bounds; Postman uses the controller's JSON example. The complete API HTTP fixture passes normally and under ASan/UBSan; generation smoke, all 42 language tests, and the three-stage macOS bootstrap pass. Error paths identify nested fields without array indexes. Primitive typed-array request fields beyond TextList remain unsupported.
2026-10-27 typed Product lookup update: json_shape(Text) now strictly decodes bounded stored JSON into a declared shape with nested and optional fields. Product find returns Result[ProductRecord, ApiError] across controller, service, repository interface, and implementation; generated OpenAPI describes the typed response. Focused decoder tests pass normally and under ASan/UBSan, and fixture yaza check, generation smoke, and normal HTTP CRUD smoke pass. The full macOS bootstrap passes three self-hosted compiler stages, 40/40 language tests, API layer/generation checks, and normal plus ASan/UBSan HTTP smokes. At that earlier milestone, Product list still returned JSON Text; the later typed collection update supersedes that limitation.
2026-10-27 request DTO update: The Product controller now constructs a shared ProductInput from validated HTTP path/body values and passes that typed value through service and repository functions; pagination remains explicit. The Product repository no longer reads ambient HTTP state, and DELETE 204 is route metadata rather than repository behavior. Compiler conformance requires the request DTO in the five CRUD method contracts and checks the bound implementation signatures. The three-stage macOS bootstrap passed with byte-identical generated C, all 39 language tests, API layer/generation conformance, and normal plus ASan/UBSan HTTP smokes. This is a single-case DTO path, not the RFC's general typed request/response contract or runtime-polymorphic dependency dispatch.
2026-10-25 implementation update: The HTTP runtime now has an optional per-client IPv4 token bucket with bounded state and computed Retry-After responses. It is a tested global process-local limit, not the RFC's composable middleware policy or a distributed limiter.
2026-10-26 implementation update: std/pg/errors.yz classifies validated PostgreSQL SQLSTATE values without exposing server messages. The Product repository maps unique violations to AlreadyExists, other integrity constraints to Conflict, and query cancellation/timeouts to Timeout; other cases remain Unavailable. Unit tests and live PostgreSQL 18.6 HTTP fixtures verify SQLSTATE 23505, 23514, and 57014 as safe 409, 409, and 504 responses. The container smoke also terminates PostgreSQL during an active create and verifies a safe 503 unavailable, then restarts PostgreSQL and verifies /readyz returns 200 in the same API process. Automatic retry of the interrupted create is not implemented; it remains a 503 to avoid repeating a potentially non-idempotent operation.
2026-10-26 implementation update: Service and repository interface methods declare bind <method> to <function>. The compiler requires each target to be declared in the matching implementation file and reachable through the expanded application module graph. It compares supported parameter counts and types plus return types across interface metadata, layer metadata, and the bound executable function. Product list signatures carry typed limit and offset from controller through service and repository into paginated PostgreSQL and in-memory operations; service-level bounds produce the same InvalidInput result before either storage path. Layer conformance accepts the template and rejects missing/duplicate bindings, missing functions, arity/type/return mismatches, incorrect service-to-repository delegation, and controller actions calling the wrong service method. Generation and no-database HTTP smokes pass, and the PostgreSQL 18 container HTTP smoke passes migrations, query execution, pagination, and lifecycle/restart recovery. Dispatch remains static: runtime polymorphism, generic trait resolution, general typed Product results, and fully YAZA-owned storage remain unimplemented.
2026-10-26 implementation update: std/db/core.yz now defines tagged DbValue/DbCapabilities contracts, stable database error codes, explicit SQL NULL, and checked typed accessors. std/db/postgres.yz converts those values into PostgreSQL's bounded typed Bind parameters; SQL NULL requires an explicit OID. Its result adapter decodes known integer, Boolean, finite Float64, text, and bytea columns into DbValue, rejecting unsupported OIDs and invalid floats. Adapter errors use fixed DbError categories/messages and do not expose PostgreSQL server text. Normal and ASan/UBSan database-core tests cover value/type mismatches, capability invariants, binary data, parameter OIDs, row values, and rejection cases. Provider-neutral connection/session contracts, schema-to-shape mapping, pooling, and additional engines remain unimplemented.
2026-10-26 implementation update: std/db/query.yz now represents bounded PostgreSQL SELECT operations as YAZA data: explicit projected identifiers, one comparison predicate, ordering, and pagination. std/db/postgres.yz quotes only validated ASCII identifiers, emits comparison operators from a closed variant, binds non-NULL values separately, and handles SQL NULL with IS NULL/IS NOT NULL. Safe error conversion retains only validated SQLSTATE codes. Focused query tests pass. This is the runtime lowering foundation, not the accepted native query-block syntax or a schema-aware result mapper; multiple predicates, joins, generated schema bindings, and provider-neutral session lifecycle remain open.
2026-10-26 implementation update: std/pg/pool.yz and the native runtime now provide a bounded process-local PostgreSQL pool with SHA-256 connection identities, per-database and process-wide caps, bounded acquisition and idle timeouts, health checks for reused connections, discard on failure, stats, and bounded shutdown draining. The Product repository uses it for CRUD and lifecycle hooks. The C pool harness covers saturation, timeout, reuse, health checks, waiter wakeup, idle expiry, discard, stats, and shutdown; the PostgreSQL 18.6 container API smoke passed with the pool active. The pool uses thread-safe coordination, but the API remains single-threaded and the evidence does not establish concurrent handler throughput or nonblocking database I/O. Provider-neutral pool/session abstractions, remote TLS evidence, Linux execution, and the wider database matrix remain open.
2026-10-27 implementation update: the no-database Product repository now calls std/api/memory_records.yz for list/find/create/replace/patch/delete and returns typed Result outcomes. A bounded native adapter mutates the server's in-memory table; it does not write HTTP responses. Handler results flow through the normal response serializer. Starter CRUD and the complete no-database API fixture pass, including chunked JSON creation. The adapter remains demo storage, not general YAZA-owned data structures or persistence. The macOS self-hosted bootstrap passed three compiler stages, byte-identical generated C, all 39 language tests, and API generator/auth/HTTP smoke checks. The PostgreSQL container smoke was not part of this bootstrap run.
2026-10-28 indexed validation-path update: Invalid elements in TextList, List[Shape], and primitive typed-array request bodies now include a zero-based item index, such as lines[1].quantity. The runtime accepts only canonical indexes 0–63 in safe field paths, rejects leading zeros and out-of-range values, and retains the 128-byte path bound. OpenAPI error schemas use the same indexed grammar. API smokes cover first/second shape items, each primitive array type, TextList, typed InvalidInput and ValidationFailed paths, and rejection of unsafe indexes; generator checks cover valid and invalid index forms. The full macOS bootstrap passes, including all 42 language tests, generation checks, and normal plus ASan/UBSan API HTTP smokes. Malformed containers and array-level item-count violations report the array field without an item index.
Summary
Turn API route/controller/service/repository declarations into executable, typed application code. Define request binding, validation, dependency construction, typed PostgreSQL access, pooling, migrations, transactions, configuration, and test seams without making those concerns hidden compiler magic.
Motivation
The current API sample offers working CRUD, but some behavior still belongs to the generic runtime. All six Product CRUD routes invoke explicit YAZA controller → service → repository functions, and API smoke verifies the calls. The compiler checks layer/interface metadata and static function bindings, but the declarations are not runtime polymorphism. Product create/replace/patch/find return ProductRecord, and delete returns a typed ProductDeleteOutcome behind its bodyless 204 response; only list still exposes JSON text. With DATABASE_URL, repository operations and startup/schema/readiness hooks use the pure-YAZA PostgreSQL client and bounded pool; generated API binaries no longer link libpq. Without a database, ProductRepository calls std/api/memory_records.yz, whose bounded native adapter mutates the in-memory table and returns data for normal handler serialization. The HTTP response writer remains native, and the in-memory table is not general YAZA-owned storage.
Proposal
- A route binds method/path to a controller method. The compiler verifies the request and response types, authorization policy, and service dependencies at build time.
- Path/query/header/body values are parsed into typed request values. Missing, malformed, and invalid values produce typed validation errors before a controller runs. JSON unknown-field behavior is explicit per shape; the default for public APIs is reject unknown fields.
- Controllers translate transport inputs/outputs and call service methods. Services own use cases and return
Result; repositories own persistence. Each layer is executable source, not a filename convention or compiler metadata substitute. - Dependency construction is explicit in a composition root. Lifetimes are limited to application, request, or transient; request-scoped resources cannot escape into application scope.
- PostgreSQL uses a pool with bounded size, acquisition timeout, idle timeout, cancellation, and shutdown draining. Pool sizing and timeouts are configurable and observable.
- SQL parameters are always bound separately from SQL text. A typed query declaration maps columns to a declared shape and fails on incompatible nullability or missing columns. Dynamic identifiers are not interpolated; they require checked identifier values from a finite allowlist.
- Compile-time schema checks use a checked-in offline schema snapshot; compiling does not require a live database. The snapshot format and refresh workflow are versioned and tied to the target PostgreSQL version.
- Transactions are explicit and scoped. Nested transactions use savepoints or fail clearly; commit failure is returned. Retry is limited to classified transient errors and never repeats a non-idempotent operation without explicit policy.
- Migrations are ordered, transactional, checksummed, and forward-only by default. The current prototype's ordered checksum check is a partial implementation of this proposal; migrations remain external and are not a normative language guarantee. Embedded migrations are optional and must retain checksums. Destructive operations require explicit review; production migrations are not run implicitly at server startup.
- Configuration precedence is defaults, configuration file,
.envfor local development, then process environment.Secret[T]redacts formatting/log output and cannot be serialized by default. Production secret-manager integrations remain adapters. - Middleware is an explicit typed chain with defined order and short-circuit behavior. Authentication, authorization, CORS, rate limiting, request IDs, timeout, and error mapping must not depend on incidental declaration order.
yaza generate --postmanand OpenAPI generation use the same route/type metadata used by the compiler. Generated collections include path/query/header/body examples and declared security schemes, and generation fails for routes that cannot be represented faithfully.- API templates include unit, HTTP integration, and PostgreSQL integration test commands. The framework provides controllable test server/database seams but does not silently start or mutate a developer's database.
- Structured logging and metrics/tracing use typed fields and redact
Secret/Piivalues by default. A bounded TTL cache is an optional adapter with explicit invalidation; cached authorization decisions are disabled unless a policy defines actor, resource, and freshness keys. - Upload handling, gRPC, queues, schedulers, WebSockets, and SSE are optional packages, not required pieces of the smallest HTTP server. Each package must specify size/retry/dead-letter/timeout limits and structured shutdown before it is considered production-ready. Their absence must be visible in
yaza check --featuresand generated docs.
Illustrative syntax only:
route GET "/api/products/{id}" -> ProductController.get
controller ProductController:
service: ProductService
fn get(request: Request[ProductId]) -> Result[Response[Product], ApiError]:
let product = self.service.get(request.path.id)?
return response.ok(product)
interface ProductRepository:
fn find(id: ProductId) -> Result[Option[Product], StorageError]
The exact DSL is not selected. Equivalent ordinary functions and attributes may prove simpler and more transparent.
Security considerations
Generated API code must not bypass application authorization or validation. SQL binding does not prevent business-logic authorization defects. Errors and logs must redact secrets and personal data. Migrations, pool limits, request bounds, and cancellation are part of the operational security boundary.
Staged implementation
- Bind typed route values and make one controller call one real service and repository method.
- Move CRUD behavior out of generic runtime special cases into the API template/library.
- Complete schema-mapped PostgreSQL queries, pool limits, rollback-scoped transactions, and live migration/transaction integration tests.
- Add typed configuration, secrets, middleware, authorization, and generated OpenAPI/Postman consistency checks.
- Add reusable HTTP/DB testing and production deployment adapters only after the core contracts stabilize.
Implementation progress
2026-10-28 — Bounded TTL-cache slice:
std/cache/ttl.yzexposes get/set/remove/clear over fixed process-local storage (128 entries, 255-byte keys, 4095-byte values, TTL from 1 ms to seven days). Expiry uses monotonic time; a mutex protects concurrent callers; live capacity pressure evicts the least-recently-used entry; entry bytes are wiped on removal, replacement, expiry, clear, and shutdown. Language tests and a C runtime harness cover public operations, expiry, capacity, and concurrent access; normal and ASan/UBSan checks pass as part of the macOS bootstrap. This cache is volatile and local to one process, does not coordinate invalidation between instances, and must not be used for secrets or authorization decisions. Other G.12 capabilities and this RFC remain partial.2026-10-25: Executable API handlers may now return
Result[Text, E].ok(Text)follows JSON validation; everyerr(E)maps to a fixed generic500 Internal Server Errorenvelope without exposing the error payload. A layered Result-greeting example and HTTP smoke cover both outcomes and pass normally and under ASan/UBSan. This is an initial safe failure boundary; typedApiErrorstatus mapping, declared response schemas, typed repository dispatch, and API-owned Product persistence remain incomplete.2026-10-25: The API template now maps known unit cases of
ApiErrorfromResult[Text, ApiError]to fixed safe 400/401/403/404/409/422/429/500/503 envelopes. OpenAPI generation describes the same responses; 401 includesWWW-Authenticate, while 429/503 includeRetry-After. Unknown cases and other error types retain the generic 500 response without exposing error payloads. Payload-bearing error validation, model-derived response types, and unified framework/auth/database error conversion remain incomplete.2026-10-25:
ApiError.InvalidInput(field: Text)now emits its field path in a 422 response only when it matches a bounded ASCII dotted-identifier grammar. Invalid field text is omitted, while status and stable error code remain. OpenAPI documents the field grammar; normal and ASan/UBSan HTTP smokes verify safe and rejected field paths. At that point,ValidationFailedpayload handling, model-derived response types, and unified framework/auth/database error conversion remained incomplete.2026-10-26: All declared
ApiErrorvariants now have fixed status/code responses.InvalidInput.fieldandValidationFailed.errorsare emitted only after bounded safe-field validation. DirectShape,Option[Shape], andResult[Shape, ApiError]controller results serialize supported nested fields, and OpenAPI derives their success/error schemas. A layered Result-shape sample route exercises success, not-found, and validation outcomes; API HTTP smoke passes normally and with ASan/UBSan, and OpenAPI/Postman generation smoke passes against the workspace-built self-hosted CLI. Shared response envelopes and unified framework/auth/database error conversion remain incomplete.2026-10-26: The native HTTP response writer now converts legacy JSON string error bodies to a common nested
error.type/error.codeenvelope by status. This covers authentication, missing routes/methods, optional-handler absence, and generic PostgreSQL/service failures while preserving already-structuredApiErrorand validation bodies. It reuses the shared response path for security headers, CORS, connection-close behavior, and logging. Normal and ASan/UBSan HTTP smoke passes. Typed source conversion remains incomplete.2026-10-26: OpenAPI generation now adds common 400/401/404/405/413/415/422/429/431/500/503 error responses to every route operation, with stable type/code schemas, validation field patterns, bearer challenge, and retry headers. Route-specific
ApiErrormappings take precedence where they provide more precise codes. The API generation smoke verifies the shared responses for ordinary and typed-result endpoints.2026-10-26: API request shapes now support bounded anchored ASCII
patternvalidation forTextandOption[Text], with literal/class/range/negation atoms and?,*,+quantifiers. The compiler rejects unsupported patterns and non-Text fields; runtime matching uses bounded dynamic programming with no backtracking. OpenAPI carries the pattern and mismatches return a fixed safe field code. The regex unit test, incompatible-type/syntax fixtures, and OpenAPI/Postman generation smoke pass. Full HTTP loopback smoke passed normally and under ASan/UBSan.2026-10-25: Executable route handlers may now return
Option[Text]as a typed absence signal.some(Text)follows the ordinary JSON validation and response path;nonebecomes a stable 404 JSON envelope. The Product API template includes a focused optional-greeting route, and full HTTP smoke passes for both outcomes normally and under ASan/UBSan. This extends typed API outcomes but does not yet provideResult[Response, ApiError], typed repository dispatch, or move Product persistence out of the native CRUD engine.2026-10-25: The native API runtime now refuses non-loopback IPv4 binds unless
YAZA_API_TOKENis configured; the listener host is parsed and this policy is enforced before opening the socket. Authentication helpers reject duplicate Authorization headers and compare equal-length bearer tokens in constant time. The HTTP smoke now covers unauthenticated public-bind rejection and missing, incorrect, and duplicate credentials. This improves the local-token security boundary but does not provide TLS, route-level policies, roles, or typed middleware; RFC 0004 remains incomplete.2026-10-26: API routes now support explicit
auth requiredandauth publicpolicies, with the omitted-policy behavior inherited from the configured global bearer token. Runtime startup validates required-route token configuration and route matching enforces the policy. OpenAPI and Postman generators represent required/public route security; Postman attaches a configurable bearer token to required and inherited-policy requests and omits it for public requests. A dedicated smoke covers token outcomes, startup failure, and both generators. Users, roles, actor-aware authorization, TLS, and a composable middleware pipeline remain incomplete.2026-10-23: The API template compiles controller actions and imported function bodies into the server.
GET /api/greeting/{name}binds declared Text path/query/header values asOption[Text], applies declared defaults, and executes controller → service → repository functions;json_quote(Text)safely builds the JSON response. HTTP smoke checks cover decoded values and escaping, while Postman/OpenAPI expose the same route parameters. Typed request structs, body binding, dependency scopes, typed repository results, Product CRUD implementations, and the remaining acceptance criteria are still outstanding.2026-10-23: Typed HTTP accessors now bind declared
Number,Int32,Float64, andBooleanvalues from path and query components asOption[T]. Runtime validation and accessors share controller metadata and reject invalid values, integer overflow, non-finite floats, and wrong primitive forms; omitted query values use checked typed defaults. The API'sGET /api/typed/...demonstration executes the typed values through controller → service → repository, and the HTTP smoke covers all eight accessors and invalid cases. The native compiler passed two consecutive self-host builds with byte-identical generated C, and its YAZA suite reports 16 passing tests. Request structs, body binding, DI scopes, typed repository results, executable Product implementations, typed HTTP errors, and the RFC acceptance criteria remain outstanding.2026-10-23: Controller declarations now support scalar body fields (
body field: Type) forText,Number,Int32,Float64, andBoolean. The runtime checks the declarations against the JSON example at startup, rejects missing/extra/malformed or wrong-typed fields before dispatch, and exposes each field asOption[T]. An executable POST example calls controller → service → repository and returns 201. OpenAPI emits typed request-body properties and required fields; Postman emits the raw JSON body at the standard request-body location. Normal and ASan/UBSan API smoke checks cover Unicode/escaped text, valid values, missing/extra fields, type mismatches, and numeric bounds. Shape-based/nested request contracts, general validators, typed errors, executable Product CRUD, and the RFC acceptance criteria remain outstanding.2026-10-23: API controllers can now declare
body payload: ShapeNameand handlers can read it asOption[ShapeName]withhttp_body_shape("payload"). The native compiler emits type-specific decoders for nested shapes with Text, Number, Int32, Float64, and Boolean leaves; every object level rejects missing/extra fields, malformed JSON, wrong scalar types, and out-of-range integers before dispatch. The API sample passes through a nested contract shape via controller → service → repository. OpenAPI emits nested object properties, required keys, andadditionalProperties: false; Postman carries the body example. Normal and ASan/UBSan HTTP smoke pass for valid nested data and invalid/missing/extra/numerically out-of-range fields. Two successive native compiler stages emit byte-identical C and all 16 YAZA tests pass. Optional/variant/list shape fields, declarative validation, typed errors, executable Product CRUD, and the overall RFC acceptance criteria remain outstanding.2026-10-24: Request shapes now accept optional scalar fields (
Option[Text],Option[Number],Option[Int32],Option[Float64], andOption[Boolean]) and optional nested shape fields. Omitted or explicit JSONnullvalues decode tonone; present values still pass strict type, range, required-field, and unknown-field checks. The API sample reads an optional nested Text field through controller → service → repository and validates an optional nested object. Normal and ASan/UBSan HTTP smoke passes for present, absent, null, and invalid optional values. OpenAPI 3.1 emits nullable scalar/object schemas and omits optional fields fromrequired; Postman retains the request example. The YAZA suite passes 16/16, and consecutive native compiler stages 19 and 20 emit byte-identical C. Variant/list request fields, declarative validation, typed errors, executable Product CRUD, and the overall RFC acceptance criteria remain outstanding.2026-10-24: API request shapes now support field-level
min/maxnumeric constraints andmin_length/max_lengthUnicode codepoint constraints forText, consistent with JSON Schema string lengths. The native decoder enforces constraints before dispatch; each decoded Text value also has a separate 1023-byte UTF-8 cap, documented with thex-yaza-maxUtf8BytesOpenAPI extension. Compiler checks reject unknown/duplicate rules, incompatible types, and invalid Text bounds. HTTP and generation smoke cases cover ASCII and multibyte boundaries and the generated schema. Typed validation error objects, pattern/email rules, collection constraints, and the remaining RFC acceptance criteria are still outstanding.2026-10-24: Nested request shapes now bind optional
TextListfields from JSON string arrays. The runtime limits each list to 64 items and each decoded item to 1023 UTF-8 bytes; wrong element types, wrong containers, and escaped NUL values are rejected. The sample reads the optional list in the controller and passes it through the service to the repository, and OpenAPI emits nullable array schemas with string items. Normal and ASan/UBSan HTTP smoke cover empty/null lists, 64/65 item boundaries, invalid item/container types, and the layered list count. Tagged-variant/map body fields, general collection types, and the remaining RFC acceptance criteria are not implemented.2026-10-24: Request shapes now accept
min_itemsandmax_itemsvalidation rules forTextList, bounded to the runtime's 0–64 item range. The compiler rejects incompatible field types, out-of-range or non-integer limits, duplicate rules, andmin_items > max_items. The API decoder applies declared bounds to present arrays, while omitted andnulloptional lists remainnone; OpenAPI emits matchingminItems/maxItems. The layered example enforces 1–63 labels. Normal and ASan/UBSan HTTP smoke cover empty, null, 63/64/65 item requests, and generation smoke checks the schema. Typed field-error details, other list element types, tagged variants/maps, and the remaining RFC criteria are outstanding.
2026-10-24: Structured JSON validation errors
Shape/scalar body contracts and generic in-memory/PostgreSQL CRUD now return a structured 422 response containing an error type, safe field path, and stable code. The implementation does not include submitted values; unknown keys report $ rather than reflecting attacker-controlled field names. Normal and ASan/UBSan HTTP smoke covers CRUD primitive-type, required-field, unknown-field, and nested-field errors. The YAZA suite passes 16/16 and consecutive native compiler stages emit identical C. This does not yet provide the RFC's unified typed result model or mappings for query, authentication, database, and application errors.
2026-10-24: Structured path and query validation errors
Invalid typed path, query, and header parameters now return structured 400 responses with a declared parameter name and stable code. Unknown query keys use $ and do not echo their values. The API smoke verifies Int32 overflow, invalid Float64/Boolean query values, unknown keys, and privacy; normal and ASan/UBSan HTTP smoke pass, and the language suite remains 16/16. A real local PostgreSQL run also passed the migration, CRUD validation, and restart-persistence checks normally and under ASan/UBSan. Authentication, database, and application failures still do not share a typed error model.
2026-10-24: PostgreSQL protocol and SCRAM progress
Added extended frontend Parse/Bind/Describe/Execute/Sync/Flush/Close messages and backend key data, parameter status, command tags, and transaction-status parsing in std/pg/protocol.yz. Text-format scalar decoding now handles int2/int4/int8/OID, Boolean, UTF-8 text, and bounded hex bytea with explicit SQL NULL; numeric/date values remain text to preserve precision, while binary results remain raw bytes. Added std/pg/scram.yz to construct the PostgreSQL SCRAM-SHA-256 initial response, parse bounded server-first attributes, enforce nonce binding and a 4,096–1,000,000 iteration range, derive the proof through provider-backed PBKDF2/HMAC/SHA-256, and verify the server signature with constant-time comparison. The RFC 7677 exchange vector passes along with malformed nonce/attributes/base64, low iteration count, signature tampering, username escaping, and non-ASCII rejection cases. Intermediate SCRAM key buffers and the PBKDF2 password-byte copy are explicitly wiped. Bounded TCP connect/read/write/close builtins and a loopback/timeout harness pass normally and under ASan/UBSan. Added net_tls_client with TLS 1.2+, platform system trust, hostname checks, and bounded handshakes; on macOS it uses deprecated Secure Transport and only compilation/error-path checks have run, while Linux build/handshake verification remains pending. std/pg/transport.yz now sends SSLRequest, upgrades the socket to TLS, rejects plaintext, sends the startup packet, and runs the SCRAM-SHA-256 startup state machine. Cleartext and MD5 password authentication fail closed. Live-server handshake and authentication behavior are not tested. The YAZA suite passes 22/22; crypto, SCRAM, and PostgreSQL value-codec vectors pass under ASan/UBSan. This remains protocol/authentication and scalar-codec groundwork: API projects still use libpq; successful TLS handshake validation, live state-machine/TCP integration, Unicode SASLprep, shape-to-row mapping, transaction execution, pooling, and schema snapshot generation remain outstanding.
Acceptance criteria
- A generated API project's create/read/update/delete path executes declared controller, service, and repository implementations.
- Tests prove request binding, error mapping, authorization order, parameter binding, transaction rollback, pool saturation behavior, and shutdown draining.
- Query tests include injection payloads; migration tests include checksum drift, interrupted application, and restart behavior.
- Postman/OpenAPI output is validated against the same route schema and round-trips representative request/response examples.
- A sample API can use in-memory and PostgreSQL repositories behind the same interface without runtime special cases.
Resolved architecture decisions
Accepted by the project owner on 2026-10-24. These decisions set direction; they do not claim implementation is complete.
- PostgreSQL driver: implement the PostgreSQL v3 wire protocol in a YAZA
std/pgmodule. The host runtime may provide bounded socket, TLS, and operating-system cryptography primitives, but it must not delegate protocol behavior or SQL execution tolibpq,psql, or another database client. Remote connections must verify TLS certificates; plaintext is limited to explicitly configured local development. - HTTP routes: represent routes as typed framework-library metadata that the compiler validates against controller signatures. Keep HTTP routing out of the core language grammar so applications can choose and extend a framework without reserving language keywords. The current route-file DSL is transitional metadata, not the target source of truth.
- PostgreSQL schema snapshot: use a canonical, versioned
yaza.schema.jsonsemantic catalog, recording the snapshot format version and PostgreSQL major version. Include tables, columns, PostgreSQL types, nullability, keys, and constraints in deterministic order. Keep migrations as the source of truth; format version 2 records a SHA-256 fingerprint over ordered migration filenames and content checksums, and offline compilation rejects a mismatch. Snapshot generation requiresmigrations/and verifies that the PostgreSQL migration ledger matches the checked-in migration set before recording the fingerprint. Do not use rawpg_dumptext as the compiler contract because its formatting and details can vary by server/tool version.
Open questions
No architecture choices remain open in this RFC. The selected driver, route metadata contract, schema snapshot, pool behavior, and database test seams still require implementation and conformance evidence.
2026-10-24: Pure-YAZA PostgreSQL wire-codec foundation
Added bytes_from_number(Number) with a checked 0–255 range, bytes_to_text_checked(Bytes) -> Option[Text], and std/pg/protocol.yz. The YAZA module constructs PostgreSQL v3 startup, simple-query, SSL/GSSENC negotiation, and cancel messages; serializes and reads bounded big-endian integers; rejects truncated, oversized, non-ASCII-tagged, or non-exact backend frames; classifies AuthenticationOk, cleartext, MD5, SASL, SASL continuation/final, and unsupported authentication codes; parses bounded RowDescription metadata with checked UTF-8 column names and text/binary format codes; validates ErrorResponse framing and SQLSTATEs; and validates DataRow lengths while preserving SQL NULL as none. It validates SASL mechanism lists' ASCII/NUL framing. tests/pg_protocol.test.yz passes normal execution and ASan/UBSan for startup/query and negotiation framing, integer boundaries, truncation, authentication, RowDescription, ErrorResponse, DataRow values/NULL/bounds, and malformed UTF-8. Leak detection was disabled for that sanitizer run because current Bytes values do not have deterministic reclamation. The API still links libpq; no socket/TLS transport, SCRAM proof, typed SQL/result mapping, or connection pool is present, so the selected pure-YAZA driver is not implemented yet.
2026-10-24: Parameterized pure-YAZA PostgreSQL queries
Added a checked Bind builder for separately encoded bounded text parameters and explicit SQL NULL flags, plus pg_extended_query_message and pg_query_params, which send Parse/Bind/Describe/Execute/Sync and reuse the bounded result loop. pg_parse_message_with_oids, pg_extended_query_message_with_oids, and pg_query_params_typed also send validated per-parameter PostgreSQL OIDs; actual values still use the PostgreSQL text format. pg_query remains for trusted static SQL; user values should go through the bound-query functions. Unit coverage verifies parameter payload bytes/lengths, empty-string versus NULL encoding, OID count/range/serialization, mismatched/invalid flag rejection, metadata validation, and the five-message exchange order; all 22 tests pass, and focused protocol/transport test binaries pass ASan/UBSan (leak detection disabled for the arena runtime). The client still lacks native YAZA value encoders, server-backed interoperability evidence, SQL-to-shape mapping, transaction/pool APIs, and integration into the generated API's libpq CRUD path, so RFC acceptance criteria remain unmet.
2026-10-24: Checked typed-parameter construction
Added the PgParameters builder and pg_query_with_parameters. Constructors encode Text, Number as PostgreSQL int8, Int32 as int4, Float64 as float8, Boolean, Bytes as PostgreSQL hex-text bytea, and explicit SQL NULL with a caller-selected OID. The builder validates parallel-list consistency, OID ranges, the 1024-parameter cap, NUL-free non-NULL values, and the 1 MiB aggregate text-value limit before encoding Parse/Bind/Describe/Execute/Sync. The generated API connection path has not changed, and values are text-format (there is no binary Bind encoder; other OIDs can be supplied with pg_parameters_add_text_as and a caller-provided PostgreSQL text representation). The PostgreSQL protocol test exercises constructor output, OIDs, NULL, bytea, malformed parameter bundles, and message construction; the full YAZA suite passes 22/22. Added typed nullable result accessors for integer, Boolean, text, and byte values; mismatched result types return errors, while SQL NULL returns none. Accessor coverage runs in the PostgreSQL transport test. The query example uses the parameter builder, and a new transaction example demonstrates begin/query/commit with rollback cleanup on errors. Live PostgreSQL interoperability, shape-to-row mapping, pooling, automatic transaction scope cleanup, and API-driver integration are still outstanding. The pure-YAZA transport now exposes manual begin/query/commit/rollback and savepoint operations; begin probes for idle state and rejects nested BEGIN, and commit checks the server completion tag and ReadyForQuery state. Savepoint identifiers are restricted to 1–63 ASCII identifier bytes. Transaction helper tests cover command/status matching, failed-commit classification, and identifier length/injection boundaries; ASan/UBSan passes with leak detection disabled. Added pg_execute_command_script for trusted command-only scripts, with a 10,000-command cap and completed-command count; normal queries still reject multiple commands. Multi-command result sets remain unsupported. The full YAZA suite passes 22/22, but this protocol path has no live PostgreSQL verification. These operations still lack live-server exchange evidence and automatic rollback on scope exit. The CLI migration command has since been moved to the pure-YAZA transport, as recorded below.
2026-10-24: Native CLI migrations use the YAZA PostgreSQL client
compiler/native_cli.yz now imports std/pg/transport and runs yaza db migrate/status over the YAZA PostgreSQL v3 wire implementation instead of invoking psql. It creates and reads the migration ledger, applies each SQL file in a managed transaction, records version/checksum through bound parameters, computes SHA-256 with the OS-backed runtime primitive, and closes the connection on normal and handled-error paths. A lexical preflight rejects common top-level transaction-control statements in migration SQL. Compiler checking and two consecutive native builds passed with byte-identical generated C. There is no live PostgreSQL/TLS verification yet; API CRUD still uses libpq, plaintext loopback connections are not implemented, and embedded migrations, pooling, row mapping, Unicode SASLprep, and automatic transaction-scope cleanup remain open.
2026-10-24: Canonical PostgreSQL schema snapshot generation
yaza db snapshot [output.json] now reads PostgreSQL catalogs through std/pg/transport and writes yaza.schema.json by default. The versioned JSON catalog records the PostgreSQL major version and deterministically orders tables, columns, and constraints; each table records schema/name, PostgreSQL type, nullability, default expression, and key/constraint definitions with deferrability and validation flags. Catalog access fixes search_path to pg_catalog, uses qualified catalog functions, and specifies C collation for name ordering. Constraint definitions request PostgreSQL's non-pretty form. The CLI compiler-checks and native-builds, but no live server has verified catalog SQL/output. Offline snapshot parsing, stale-snapshot rejection, typed SQL-to-shape checking, catalog compatibility testing across PostgreSQL major versions, and atomic snapshot replacement remain unimplemented.
2026-10-24: Offline snapshot format validation
yaza db validate-snapshot [file.json] now validates snapshot format version 1 without a database connection. yaza check, yaza build, and yaza run automatically validate yaza.schema.json when it exists at the project root. Validation uses the runtime's strict JSON parser and checks the version, PostgreSQL version string, and required table/column/constraint fields. A valid sample snapshot was accepted, and an unsupported format version was rejected. This does not compare the snapshot against the live database or migration files; stale-snapshot detection, typed SQL-to-shape checking, live PostgreSQL catalog verification, and atomic snapshot replacement remain unimplemented.
2026-10-24: Migration-fingerprinted snapshots reject stale migration sets
Snapshot format version 2 adds a SHA-256 fingerprint over the sorted records filename|sha256(contents). Offline yaza db validate-snapshot and project check/build/run compare that fingerprint with the current migrations/ files and reject a mismatch. yaza db snapshot compares the local ordered migration/checksum records with the PostgreSQL ledger before querying catalogs, then checks that the files did not change during generation. A local project fixture passed with a matching fingerprint and was rejected after its migration contents changed. This protects the migration source-of-truth workflow but cannot detect manual database DDL outside the migration chain; live ledger/catalog execution, typed SQL-to-shape checking, and atomic snapshot replacement remain open.
2026-10-24: Atomic schema snapshot replacement
yaza db snapshot now writes to a unique temporary file in the target directory, flushes it, and atomically renames it over the destination. Existing regular-file permissions are preserved; a newly created snapshot uses owner-only permissions. The shared file_write_atomic(Text, Text) runtime primitive passed a local replacement smoke, including preservation of an existing 0640 mode. This verifies the filesystem primitive on macOS, not snapshot generation against a live PostgreSQL server; catalog and ledger integration remain unverified.
2026-10-24: Product CRUD route dispatches through YAZA layers (superseded)
At that milestone, repository functions validated/dispatched through the native http_repository_respond boundary, which also emitted responses. The 2026-10-27 update above replaces this with a YAZA memory-record adapter returning Result values and ordinary handler response serialization. This remains an execution-path milestone, not completion of the RFC: storage tables are runtime-owned and interfaces are static compile-time bindings.
2026-10-25: API layer declaration conformance
yaza check now validates service/repository metadata headers against their component names, verifies interface file headers, rejects duplicate method names, enforces declaration order, and requires each interface signature to match exactly one implementing metadata signature. tests/api-layer-conformance-smoke.sh accepts the valid generated project and rejects duplicate interface/repository methods, malformed method headers, out-of-order declarations, interface/service name mismatches, and repository signature mismatches with both normal and ASan/UBSan compiler builds. This makes metadata checking one-to-one and testable; it does not add typed interface dispatch or move CRUD behavior out of the native generic runtime.
2026-10-25: Explicit loopback plaintext for local PostgreSQL development
The PostgreSQL transport now permits plaintext only when PGSSLMODE=disable is explicitly configured and the requested host is exactly 127.0.0.1, localhost, or ::1. After connecting, the runtime checks the actual peer address with getpeername and accepts IPv4 127/8, IPv6 loopback, and IPv4-mapped loopback only. Remote connections continue to use certificate- and hostname-verified TLS; a server that refuses TLS does not trigger an automatic plaintext fallback. The Docker development example now opts into loopback plaintext explicitly. A local protocol fixture verifies startup, SELECT 1, typed result decoding, and Terminate in normal and ASan/UBSan bootstrap runs. This is evidence for the limited local exchange, not live PostgreSQL interoperability. The generated API CRUD path still uses libpq and is not migrated to the YAZA driver.
2026-10-25: Enforce the same database transport boundary in API libpq mode
The API's current libpq connection path now parses connection settings before connecting. Remote database connections must use sslmode=verify-full, and the runtime confirms that SSL is active after connection. Modes that can fall back to plaintext or omit host-name verification—including prefer, allow, require, verify-ca, and the default—are rejected before network access. Local plaintext requires explicit sslmode=disable, an explicit numeric loopback host, and a post-connect check of the actual peer address; the development template uses 127.0.0.1. Normal and ASan/UBSan HTTP smokes confirm that remote disable, prefer, allow, require, and verify-ca modes are rejected before connection and that the database password is not logged. No successful remote TLS or live PostgreSQL connection was available for integration verification. This hardens the existing adapter but does not replace libpq with the selected YAZA PostgreSQL client.
2026-10-25: YAZA PostgreSQL CRUD reaches a live local server
The generated Product repository now calls std/pg/api_records.yz for list/get/create/replace/patch/delete when DATABASE_URL is set. Its Product controller declares typed body fields so the YAZA repository can read the validated request values. yaza db migrate applied the API migration, and the complete API smoke passed against an isolated local PostgreSQL 17.6 database, including chunked JSON, safe validation responses, all CRUD operations, DELETE 204, and persistence after restarting the API. The disposable role and database were removed. This proves the local loopback/plaintext path for one server version; it does not satisfy remote TLS, rollback/failure, automated CI, or cross-version gates. API startup/schema/readiness still use libpq, and typed interface dispatch, typed model results, connection pooling, and migration/readiness unification remain incomplete.
2026-10-26: Route-level success status declarations
API routes now accept status 200, 201, 202, or 204 between the path and target. The declared status is included in runtime route metadata and generated OpenAPI, takes precedence over method defaults and a conflicting http_set_status call, and is validated when the server starts. A 204 response skips JSON body validation and emits no body or content headers. The isolated API fixture covers GET and POST 202, controller and handler targets, setter precedence, generated OpenAPI, and bodyless 204; macOS bootstrap passed with the feature enabled. Broader status codes and a unified typed response contract remain incomplete.
2026-10-26: Pure-YAZA API lifecycle hooks
The API metadata accepts startup <function> and readyz <function>. The compiler requires reachable zero-argument hooks with Number and Boolean return types, respectively. The generated server invokes startup before listening and delegates /readyz to the declared YAZA function. The API template uses the pure-YAZA PostgreSQL client to configure bounded session timeouts, check the migrated table, seed demo records idempotently, and probe readiness with a 500 ms per-operation budget; readiness failures return a stable PostgreSQL-specific body without exposing connection details. Generated API binaries no longer define YAZA_WITH_POSTGRES or link libpq; macOS dependency and symbol inspection confirms this. Compiler lifecycle conformance, transport-policy tests, language/API suites, generation smoke, and no-database HTTP smoke pass. After the hook refactor, yaza db migrate and the full database-configured API HTTP smoke passed on a disposable PostgreSQL 18.6 container, verifying startup seeding, /readyz, CRUD, restart persistence, readiness failure/recovery during a pause/resume, live SQLSTATE mappings, and safe 503 handling when PostgreSQL is terminated during an active create. It then restarts PostgreSQL on the same published host port and verifies /readyz recovers in the same API process. This covers one local plaintext configuration, not TLS or the wider server/version matrix; retry of the failed create remains intentionally absent pending an idempotency contract.