Repository navigation
Description
Current scope (v2026.10.1-alpha)
The basic managed-extension capability has shipped (f0746ff and subsequent hardening): a Onebox-owned PostgreSQL 18 image, extension declarations, dependency/preload settings, availability checks, reconciliation before application migrations, refusal of incompatible installed/candidate versions, preload-removal protection, and extension checks during restore.
The image-publication decision is made and the backup subsystem is executable. Neither should remain framed as an unstarted dependency.
This issue remains open for the outstanding proof and reporting requirements in the original proposal:
- Report observed available/enabled extension versions through a suitable server-facing read surface. Current
ob statusreports service health, not these extension facts; local-onlyob doctormust not silently become a remote probe. - Add end-to-end proof for plain PostgreSQL 18 adoption, vector data/index persistence, backup and isolated restore, and rollback with the supported managed image.
- Document the implemented compatibility and deliberate upgrade/removal refusal boundary, and distinguish it from automatic extension upgrades.
No PostgreSQL-extension/vector scenario was found in the current server E2E fixtures. Keep this open for those remaining requirements rather than treating the original feature as wholly absent or wholly proven.
Original proposal and historical context
Objective
Let the managed PostgreSQL driver provide a closed, versioned set of compiled extensions, starting with pgvector, without weakening the managed-service boundary into arbitrary images or runtime package installation.
This should be a general Onebox capability with PostgreSQL as the first implementation, not a Monk-specific exception.
Why
Today services: {postgres: 18} fixes the service image to postgres:18. Driver settings become PostgreSQL -c parameters; there is no extension, variant, image, package, or initialization-hook contract. Extensions that need server binaries therefore force the database into a user-owned daemon workload.
The repository already documents this boundary in e2e/apps/immich.yml: its vector-enabled PostgreSQL image cannot use the managed driver. Monk has the same requirement for its proposed hybrid PostgreSQL full-text + pgvector research store, while its current custom PostgreSQL 18 image also carries WAL-G.
Onebox should be able to own a vector-capable PostgreSQL service completely: image provenance, compatibility, lifecycle, credentials, persistence, health, planning, and recovery evidence.
Contract direction
Add a driver-owned capability surface rather than overloading settings. The exact authored shape needs design, but it should express an allowlisted extension and a pinned version, for example:
services:
postgres:
version: 18
features:
extensions:
vector: PINNED_VERSIONThe driver resolves that declaration to a known digest-pinned image. It must not accept an arbitrary repository, Dockerfile, package name, shell command, or unbounded extension identifier. If Onebox cannot own a combination, validation refuses it and directs the user to a daemon workload.
Application migrations remain responsible for enabling an available extension in the intended database:
CREATE EXTENSION IF NOT EXISTS vector;Onebox provides and verifies the compiled extension; it does not silently mutate every database.
Requirements
Driver and image ownership
- Define a closed per-driver extension catalogue, starting with
postgres/vector. - Bind PostgreSQL major versions to explicitly supported extension versions.
- Resolve each supported combination to a signed or otherwise provenance-verifiable, digest-pinned image.
- Build or resolve images before production; never install packages with
ob exec, container startup scripts, or a production build. - Include the resolved extension identity and image digest in plan, canonical state, and drift comparison.
- Reject unknown extensions, unsupported combinations, downgrades, and removal when safe continuity cannot be proven.
Runtime semantics
- Preserve the existing managed-service ownership model: separate Compose project, durable volume, target-generated credential, health gate, and injected connection parts.
- Verify the declared extension is present in
pg_available_extensionsbefore the service is considered ready. - Report available and enabled extension versions through status/doctor without exposing credentials.
- Keep database-level
CREATE EXTENSIONand extension-schema migrations under application ownership.
Lifecycle and adoption
- Plan extension installation, upgrade, downgrade, and removal as explicit stateful changes.
- Preflight the existing cluster for enabled extension versions and incompatible objects.
- Support moving an existing same-major PostgreSQL service to an extension-capable managed image without replacing or silently renaming its data volume.
- Document dump/restore when in-place adoption is not safe.
- Exercise rollback before declaring an extension combination supported.
Protection dependency
Managed PostgreSQL currently has no executable backup or restore contract. #63 tracks that existing subsystem. This issue must integrate with it rather than invent a second backup path.
- Do not describe extension-capable PostgreSQL as production-managed until the selected service has current backup evidence and a passing isolated restore proof.
- Backup and restore must preserve extension metadata and use an image containing the same compatible binaries.
- A stateful extension change must participate in the migration-backup gate.
First implementation: pgvector
Use pgvector as the first extension because it exercises the whole contract:
- compiled server binaries tied to PostgreSQL major version;
- database-level enablement through
CREATE EXTENSION vector; - extension-version upgrades;
- vector column, exact search, and HNSW/IVFFlat index persistence;
- backup and restore into an extension-capable image;
- measurable image, WAL, storage, and upgrade effects.
The implementation should not assume vector-only retrieval or make pgvector an application framework. It only supplies a trustworthy PostgreSQL capability.
Non-goals
- Arbitrary custom images inside
services. - Runtime
apt, source compilation, or mutable package installation. - Automatically enabling extensions in every database.
- Treating a migration success as backup or restore proof.
- Adding a Monk-specific driver or hidden exception.
- Claiming general support for PostGIS, TimescaleDB, VectorChord, or other extensions before each has its own compatibility and lifecycle contract.
Delivery slices
- Contract: choose and document the driver-owned feature shape, compatibility model, plan representation, and refusal behavior.
- Artifact: publish and resolve the first PostgreSQL 18 + pinned pgvector image with provenance and digest binding.
- Lifecycle: add availability health, status/doctor facts, drift detection, and upgrade/removal preflights.
- Protection: connect the image and extension identity to Protection layer: 4,865 lines in the tree, no command reaches it, no spec on main, no tracking issue #63 backup, restore, and migration gates.
- Adoption: prove a plain PostgreSQL 18 data volume can move to the vector-capable image, enable pgvector by migration, restart, back up, restore, and roll back safely.
- Product proof: migrate one real consumer only after those gates pass. Monk is a suitable first consumer because it already owns PostgreSQL 18, WAL-G, SQLx migrations, and a proposed pgvector workload.
Verification
- Schema and loader tests accept the supported declaration and reject unknown or incompatible extensions.
- Generated service runtime uses the expected digest-pinned image and preserves existing credential/volume semantics.
- A PostgreSQL 18 service reports the pinned pgvector version in
pg_available_extensions. - An application migration enables
vector, writes vectors, performs exact distance search, creates an index, and survives restart. - Plan and drift output change when the extension or its resolved image changes.
- Unsafe removal/downgrade is refused with an actionable code.
- Existing plain PostgreSQL managed services remain byte-for-byte unchanged when no extension is declared.
- Backup and isolated restore preserve extension version, schema, data, and indexes once Protection layer: 4,865 lines in the tree, no command reaches it, no spec on main, no tracking issue #63 is executable.
- Documentation states the managed-service versus daemon boundary and never implies that arbitrary PostgreSQL add-ons are supported.
Done when
A project can declaratively request PostgreSQL 18 with a pinned pgvector capability; Onebox validates and plans a known image, runs it with normal managed-service semantics, proves extension availability, observes lifecycle drift, safely handles adoption and upgrades, and has current backup plus isolated restore evidence through #63 before production use.
Activity
vishr commentedon Aug 18, 2026
Two observations on dependency and one on the authored shape.
Slice 2 is the same commitment as #63's PostgreSQL work
"Publish and resolve the first PostgreSQL 18 + pinned pgvector image with provenance and digest binding" is the same obligation as task 7.2 of the
complete-oss-production-baselinespec that #63 tracks: poll upstream, build twice for reproducibility, emit SBOM, sign or transparency-publish provenance, atomically publish a qualified mapping, hold a publication SLO.That spec is not on
main— it survives at tagv2026.8.0:So both issues independently require Onebox to become a container image publisher, for two different images. Worth deciding once whether that is a business this project wants, and if so building one pipeline that serves both, rather than arriving at it twice.
Slices 4 and 6 are gated on a decision that has not been made
Slice 4 connects to #63's backup, restore, and migration gates, and states the requirement not to invent a second backup path. #63 is currently a question, not a subsystem: 4,865 lines that no command reaches, with restore, remove, and freeze all still open. Slice 6 depends on slice 4.
Slices 1 and 3 — the contract shape, availability health, status/doctor facts, drift detection — do not depend on #63 and could proceed. Slices 2, 4 and 6 cannot be finished until #63 is answered.
The authored shape pins a version the driver owns
This asks each project to name a pgvector version, while the issue's premise is that the driver owns image, provenance, and compatibility. It is the same duplication #75 argues against for Redis flags: requiring every application to understand internals the driver already owns.
A shape closer to that premise:
The driver's catalogue resolves the extension version for the declared PostgreSQL major, and the resolved identity still appears in plan, canonical state, and drift comparison — so reproducibility comes from the plan artifact rather than from every author tracking upstream releases. If an explicit pin is wanted for auditability, make it optional and refuse unknown values, rather than required.
Sequencing against #75
They touch
internal/app/services.goin different places — #75 changes command construction, this changes image resolution — so no conflict. #75 is small, blocked on nothing, and fixes a defect affecting all ten managed drivers today, so it is the cheaper one to land first.Not in dispute
The non-goals are the strongest part of this issue and worth preserving verbatim: no arbitrary images, no runtime package installation, no automatic
CREATE EXTENSION, no Monk-specific exception, and no claimed support for PostGIS or TimescaleDB before each has its own contract.e2e/apps/immich.ymlis real evidence of the gap this closes.vishr commentedon Aug 24, 2026
Housekeeping: the dependency stated in my comment above no longer holds as written.
#63 is closed as completed, and it was answered by deletion rather than by finishing the subsystem — the protection layer is gone from the tree, along with the symbols it orphaned (
e8362c4). So slices 4 and 6 are no longer waiting on that decision.What they should be checked against instead is the backup subsystem that shipped since:
ob backupnow hasenable,create,disable,prune,verify,restore,drillandstatus(cmd/ob/backup.go), all exercised by the server end-to-end suite, with the wal-g TLS, WAL-history and timer-removal defects fixed in #88, #91 and #92. The "do not invent a second backup path" requirement in slice 4 now has a concrete path to reuse.Slice 2 — becoming a container image publisher — is unaffected by any of this and is still the open question, though the
complete-oss-production-baselinespec it duplicated went with #63, sov2026.8.0is now the only place that reasoning survives.vishr commentedon Sep 1, 2026
Housekeeping: most of this has shipped, and the shape objection above was adopted.
f0746ff feat(postgres): add managed extensionslanded on 2026-08-29.Slice 2 — becoming a container image publisher — is answered yes.
.github/workflows/postgres-image.ymlandbuild/package/postgres.Dockerfilepublish the Onebox PostgreSQL 18 image, and.github/workflows/release.ymlwires it into the release. The open question in my first comment is closed by the decision to do it.The authored shape follows the driver-owned premise.
internal/app/types.go:293:So a project asks for the capability and the image owns the version — the shape argued for above, rather than a required per-project pin.
internal/app/postgres_extensions.goresolves dependencies (vectorscalepullsvector) and preloads (pg_cron,pgaudit,pg_stat_statements) without duplicate declarations, andinternal/app/validate.go:514refuses extensions on any version but 18.Slice 4 reuses the existing backup path rather than inventing one.
internal/engine/backup_restore.go:532verifies the recovered cluster actually has every declared extension and fails the restore when it does not.internal/engine/deploy.go:309reconciles extensions before application migrations;internal/engine/services.go:101documents deliberately not unloading libraries on removal.What remains open is worth restating in the issue body, because the description no longer matches the tree.
[-]feat(services): support driver-owned PostgreSQL extensions[/-][+]Managed PostgreSQL extensions: finish observed-version reporting and lifecycle E2E[/+]