Preflight-as-a-service (PPg) validates migrations and query Plans using a hosted Postgres cluster. Many apps depend on server extensions like pgvector or PostGIS. These capabilities live on the database, not in the application bundle. The preflight service must know which packs are required, ensure matching capabilities are present, and execute deterministically without cloning user repos or fetching packages.
- Hosted preflight needs to provision shadows with the right extensions and versions
- Bundles must be self-contained, with no network or registry access, yet convey required packs and capabilities
- Results must be consistent with local preflight and reproducible across runs and environments
- Security boundary must prevent arbitrary code execution and supply-chain risks
Define a strict bundle inclusion policy for packs that is declaration-only. Bundles enumerate required packs and capabilities but never include server-side binaries. PPg maintains a curated catalog of available packs per region and negotiates capabilities at job start. Plans and migration ops must annotate capability requirements so guardrails and provisioners can validate upfront.
A preflight bundle must include the following pack artifacts:
contract.jsoncanonical data contract withcoreHashbundle.jsonindex withcoreHash,profileTarget(e.g.postgres@XX), list of edges and plan manifestspacks/manifest.jsonpack declarations and resolved requirementsplans/**/*.jsonor ndjson for recorded Plans, each with annotations per ADR 018migrations/*/migration.jsonmigration definitions and node tasks with op IDs and argsops/*.jscustom operation implementations when present, plusops/*.manifest.jsonper ADR 041fixtures/**optional seed for shadow runs
No server extension binaries, installers, or network references allowed
{
"target": "postgres",
"packs": [
{
"id": "pgvector/pgvector",
"version": "0.7.2",
"requires": {
"ext.pgvector.version": ">=0.6.0",
"ext.pgvector.metric.cosine": true
},
"prefers": {
"ext.pgvector.index.hnsw": "try"
}
},
{
"id": "postgis/postgis",
"version": "3.4.2",
"requires": {
"ext.postgis.version": ">=3.3.0"
}
}
],
"derivedFrom": {
"coreHash": "sha256:...",
"generator": "prisma-next@1.0.0",
"capabilityKeysUsed": [
"ext.pgvector.metric.cosine",
"ext.postgis.version",
"sql.concurrentIndex"
]
}
}idfollows ADR 112 identityvendor/namerequiresandprefersuse capability keys from ADR 117derivedFromdocuments provenance and the exact capability keys referenced during local analysis
On bundle ingestion PPg performs:
-
Schema validation
- Validate
packs/manifest.jsonagainst the published JSON schema
- Validate
-
Catalog check
- Ensure each
id@versionexists in the PPg regional catalog and is installable
- Ensure each
-
Capability negotiation
- Discover actual capabilities on the target cluster per ADR 117 and compute a negotiated profile
-
Requirement match
- Verify
requiresare satisfied, honorpreferswhere possible, emit actionable warnings when not
- Verify
-
Plan and op gating
- For each Plan and migration op, verify annotated
requiresCapabilitiesare satisfied before execution
- For each Plan and migration op, verify annotated
-
Profile hash comparison
- Recompute
profileHashfrom negotiated capabilities and compare to optional bundleprofileHash. Mismatch yields a warning or failure depending on policy
- Recompute
- Pack declarations and capability requirements
- References to extension-specific operators or types in Plan annotations
- Optional declarative guardrail policies shipped by packs (JSON), evaluated by PPg guardrail engine
- Extension binaries or installers
- Any code from packs, except custom migration ops explicitly bundled as isolated ESM per ADR 041
- Network endpoints, dynamic imports, or transitive fetches
- PPg maintains a per-region catalog of pack IDs and versions with installation recipes and capability maps
- Catalog changes update the negotiated capabilities. If this alters
profileHash, PPg surfaces a drift advisory to the user - Regional differences are documented and returned in negotiation diagnostics
packs/manifest.jsonis canonicalized per ADR 106profileHash = sha256(canonicalize(negotiatedCapabilities))- If a bundle carries a locally computed
profileHash, PPg compares and reports parity. Enforcement policy defaults to warn in preflight and fail in promotion gates
- Shadow mode: PPg provisions a shadow DB with required packs enabled. If a required pack is unavailable in the region, the job fails with
E_PACK_UNAVAILABLE - EXPLAIN-only mode: Requires the same capability profile to produce realistic plans. If packs are unavailable, PPg fails with
E_CAPABILITY_NEGOTIATION_FAILEDrather than attempting a degraded parse-only mode
- No network or registry access during preflight
- Only declared ESM custom ops run in a sandbox per ADR 040
- Bundle content is signed per ADR 051 combined and verified before execution
- No raw data persisted beyond diagnostics allowed by ADR 024
- The same
packs/manifest.jsonis produced locally by the CLI when building a bundle - Local preflight negotiates capabilities against the developer database and records a
profileHash - Differences between local and PPg negotiation are surfaced as diagnostics with remediation advice
- Bundling extension binaries or container images
- Rejected due to security, portability, and operational overhead
- Inferring packs solely from SQL text
- Rejected as brittle and incomplete, especially for migration ops and guardrails
- Deterministic, declaration-only packs make preflight reproducible and secure
- Clear parity story between local and hosted environments
- Lanes and planners branch on capabilities, not targets, keeping code portable
- Users must manage a small additional manifest
- Regional catalog differences can block preflight until enabled
- Should PPg allow opt-in degraded EXPLAIN without extensions for limited lint-only scenarios
- Do we expose an API to preview regional pack availability and capability maps for IDE tooling
- How do we version and deprecate capability keys across pack releases without breaking existing bundles