Skip to content

feat(libs/go): OpenPool — one authenticated pool, for a service that owns its own tenancy - #206

Merged
antoinetoussaint-byte merged 1 commit into
mainfrom
feat/open-one-authenticated-pool
Oct 29, 2026
Merged

antoinetoussaint-byte merged 1 commit into
mainfrom
feat/open-one-authenticated-pool

Conversation

@antoinetoussaint-byte

Copy link
Copy Markdown
Contributor

Follow-up to #205, and the thing that makes it adoptable.

The problem #205 did not solve

#205 put the external-identity declaration and the pinned trust anchor in this library, where they belong. But every constructor here is opinionated:

  • Open splits a service across a reader and a writer and installs the scoped RLS factory;
  • OpenMaintenance selects an application role and refuses a privileged login.

A service with one runtime principal, which owns its own tenancy model rather than delegating it to transaction-local RLS settings, fits neither. So it called pgxpool.New itself — and in doing so re-decided the transport, the trust anchor and the credential, each of which this package already decides.

Four services in this fleet did exactly that. That is not four call sites; it is four copies of three decisions, which is precisely why a managed-database or passwordless posture cannot be rolled out by changing one thing.

Classification

Fix. OpenPool is the primitive the other two constructors already build on (parse, install credential, create, ping) — exposed rather than reimplemented. It is not a second mechanism: every refusal the opinionated constructors make about a capability's transport and credential holds here too, so it is the same decision by a shorter route, not a weaker one.

What it is

pool, close, err := postgres.OpenPool(ctx, connection, profile, options...)

The profile decides the transport and the pinned authority, the options decide the credential, the caller gets a pool. It grants no role, installs no session policy and judges no privilege — a caller wanting those wants Open or OpenMaintenance.

It pings before returning. pgxpool connects lazily, so a pool handed back before its first connection succeeded moves a wrong credential, an unverifiable server or an unreachable address from boot to the first query, where it reads as a runtime fault rather than the deployment fault it is.

The legacy zero profile stays accepted, so a service adopts the library in one call and changes its TLS posture as a separate, visible step rather than as a side effect of adoption.

Reuse

Reused wholesale: ParseConnection as the single judge of a connection, resolveExternalIdentity and installAccessTokenProvider for the credential, profileConnectionError so an explicit profile still reports a capability as unavailable rather than leaking the driver's text, configured for the option surface.

Deliberately not done: refactoring Open and OpenMaintenance onto this primitive. They would read better for it, but that is a change to two working, heavily-tested constructors with no behavioural need, and it does not belong in the change that introduces the primitive.

Tests

The refusal set is asserted through OpenPool itself rather than trusted to be inherited — two credentials for one pool, a password beside a provider, a pinned transport with no anchor, an anchor on a transport that verifies nothing, an undeclared issuer, verify-full on a pinned transport — plus a nil context and an empty connection, the eager ping against a name that does not resolve, and the zero profile not being rejected as a profile error.

Not verified

The success path needs a reachable Postgres and is not exercised here; it is covered only by the refusals and the eager-ping behaviour. No connection was opened to a managed instance by this PR.

Size

~70 hand-written non-test lines, ~110 test lines, one new file plus a README section.

…owns its own tenancy

Every constructor in this package was opinionated. Open splits a service across
a reader and a writer and installs the scoped RLS factory; OpenMaintenance
selects an application role and refuses a privileged login. A service with one
runtime principal and its own authority over whose tenant a call concerns fits
neither — so it called pgxpool.New itself, and in doing so re-decided the
transport, the trust anchor and the credential, each of which this package
already decides.

Four services in this fleet did exactly that. That is not four call sites; it is
four copies of three decisions, which is why a managed-database or passwordless
posture could not be rolled out by changing one thing.

OpenPool is the primitive underneath the other two, exposed: the profile decides
the transport and the pinned authority, the options decide the credential, the
caller gets a pool. It grants no role, installs no session policy and judges no
privilege — a caller wanting those wants Open or OpenMaintenance, and this does
not become a relaxed route to them.

It pings before returning. pgxpool connects lazily, so a pool handed back before
its first connection succeeded moves a wrong credential, an unverifiable server
or an unreachable address from boot to the first query, where it reads as a
runtime fault rather than a deployment one.

Every refusal the opinionated constructors make about a capability's transport
and credential holds here too — two credentials for one pool, a password beside
a provider, a pinned transport with no anchor, an anchor on a transport that
verifies nothing, an undeclared issuer, verify-full on a pinned transport — so
this is the same decision by a shorter route, not a weaker one. The legacy zero
profile stays accepted, so a service adopts the library in one call and changes
its TLS posture as a separate, visible step.

Not verified against a live server: the success path needs a reachable Postgres
and is exercised only by the refusals and the eager-ping behaviour here.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@antoinetoussaint-byte
antoinetoussaint-byte enabled auto-merge (squash) October 29, 2026 02:43
@antoinetoussaint-byte
antoinetoussaint-byte merged commit 7fd6614 into main Oct 29, 2026
3 checks passed
antoinetoussaint-byte added a commit that referenced this pull request Oct 29, 2026
…d its server's authority (#207)

* feat(managed-bootstrap): the migration owner declares its identity and its server's authority

The follow-up #205 named and deliberately left: bootstrap.Options grew an
external-identity declaration and a server trust anchor, but the command could
not see them, because the root module pins a published libs/go rather than
replacing it with the tree. #206 is on main, so the pin moves and the command
reads the two declarations.

Delivered as configuration beside the connection they describe, never as flags.
A Job's command line is readable by anything in its pod, and these two are the
deployment's statement about where this database is. Neither is a secret — an
issuer names who mints the credential, and a certificate authority is public
information — while a credential minted from the issuer is, which is why it
never appears here and is fetched per connection instead. Absent means the
password-authenticated behaviour every existing deployment has.

This is what lets migrations run against a managed server at all: the database
exists but its schema and its application roles do not, and creating a login in
the cloud grants CONNECT and nothing else — the in-database roles and grants are
the migration owner's work.

Verified: the root module builds and vets, and the bootstrap parity suite passes
against a real Postgres. One of its four parallel subtests failed first on
`all predefined address pools have been fully subnetted` — this machine had 30
Docker networks from concurrent worktrees, and the subtest passes alone. That
flake is the environment, not this change.

Not verified: no migration has run against a managed instance.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix: prune the superseded library entries go get left in go.sum

`go get` adds the new version's hashes without removing the old module
version's, and CI's tidiness gate runs `go mod tidy` and compares — so the
commit has to carry what tidy produces, not what go get leaves. Three stale
lines for the previous libs/go pseudo-version.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Antoine Toussaint <312536060+antoinetoussaint-byte@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant