feat: migrate PostgREST to connect_as credential model#352
feat: migrate PostgREST to connect_as credential model#352moizpgedge wants to merge 1 commit intomainfrom
Conversation
Replace auto-created `svc_{service_id}_ro/rw` service user roles for
PostgREST with the `connect_as` field — a reference to a
`database_users` entry that the customer owns and manages. This aligns
PostgREST with the credential model already used by MCP.
- `validateConnectAs` was not being called for `postgrest` service type
— fixed: all three service types (mcp, postgrest, rag) now validate
`connect_as` at the API layer.
- Added `noDefaultConnectAs` flag in validate tests to cover the
missing-`connect_as` and unknown-user error paths for PostgREST.
- `PostgRESTAuthenticatorResource`: removed `UserRoleID` field, added
`ConnectAsUsername`; `Dependencies()` now points to
`PostgresDatabaseResource` instead of `ServiceUserRole`; SQL targets
the `connect_as` user directly instead of a generated username.
- `PostgRESTConfigResource`: receives `ConnectAsUsername` /
`ConnectAsPassword` at construction time from the connect_as
database user instead of reading from `ServiceUserRole`.
- `generateMCPInstanceResources()`: `ServiceUserRole` generation is
now skipped for both `mcp` and `postgrest`; only `rag` still creates
`svc_*` roles. Per-node `PostgRESTAuthenticatorResource` instances
carry `ConnectAsUsername` directly.
- `ServiceInstanceResource.Dependencies()`: removed stale PostgREST
`ServiceUserRole` deps that blocked container start on the wrong
resource.
- `ServiceInstanceSpecResource.Dependencies()`: removed PostgREST
`ServiceUserRole` deps; added `PostgRESTPreflightResourceIdentifier`
— this ensures the container does not start until the database exists,
fixing a race condition where PostgREST failed with "database does
not exist" when Patroni had not finished bootstrapping.
- `ServiceInstanceSpecResource.populateCredentials()`: simplified —
all current service types use config-file credentials, so
`s.Credentials` is always nil.
only 5xx responses use `Error`. Eliminates spurious ERR flood from
409 conflict responses during cluster initialization.
- `docs/services/index.md`: replaced `svc_*_ro/rw` credential section
with `connect_as` model description.
- `docs/services/managing.md`: updated `service_id` field description,
added `connect_as` row to spec table, added `database_users` +
`connect_as` to PostgREST example, updated removal warning.
- `docs/services/postgrest.md`: updated overview, all three examples
now include `database_users` and `connect_as: "app"`, updated JWT
role-claim note to reference `connect_as` user.
- `e2e/postgrest_test.go`: renamed `TestPostgRESTServiceUserRoles` →
`TestPostgRESTAuthenticatorRole`; assertions now verify NOINHERIT and
anon-role grant on the `connect_as` user (`admin`) instead of `svc_*`
roles; `TestPostgRESTRemove` verifies `connect_as` user is NOT dropped;
`TestPostgRESTMultiHtDBURI` checks authenticator setup instead of
`svc_*` role existence; all specs now include `ConnectAs: "admin"`.
- `postgrest_service_config_test.go`: updated fixture username from
`svc_pgrest` to `myapp` to reflect the connect_as model.
- New: `postgrest_preflight_resource_test.go`,
`postgrest_authenticator_resource_test.go`,
`postgrest_config_resource_test.go` — unit tests for all three
PostgREST-specific resource types (37 tests).
PLAT-553
📝 WalkthroughWalkthroughThe pull request transitions PostgREST database credential management from Control Plane auto-provisioned per-service users to a customer-managed model where services explicitly specify a database user via a Changes
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Up to standards ✅🟢 Issues
|
| Metric | Results |
|---|---|
| Complexity | -4 |
| Duplication | 1 |
TIP This summary will be updated as you push new changes. Give us feedback
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
server/internal/orchestrator/swarm/service_instance.go (1)
38-38:⚠️ Potential issue | 🟡 MinorStale comment on
ConnectAsUsername.The comment says the field is populated only for RAG, but
Refresh()at line 84 setss.ConnectAsUsername = desired.ServiceSpec.ConnectAsunconditionally, and this PR extendsconnect_astomcpandpostgrest. Consider updating to reflect that it mirrorsServiceSpec.ConnectAsfor all service types that populate it.📝 Suggested comment update
- ConnectAsUsername string `json:"connect_as_username"` // Non-empty when RAG uses connect_as credentials + ConnectAsUsername string `json:"connect_as_username"` // Mirrors ServiceSpec.ConnectAs (set by Refresh)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@server/internal/orchestrator/swarm/service_instance.go` at line 38, The struct field ConnectAsUsername has a stale comment limiting it to RAG, but Refresh() sets s.ConnectAsUsername = desired.ServiceSpec.ConnectAs unconditionally and the PR extends connect_as usage to mcp and postgrest; update the comment on ConnectAsUsername in service_instance.go to state that it mirrors ServiceSpec.ConnectAs for any service type that populates connect_as (e.g., RAG, MCP, PostgREST) and remove the RAG-only wording, and mention Refresh() as the place that copies the value.docs/services/managing.md (1)
160-172:⚠️ Potential issue | 🟡 MinorContradictory wording in "Removing a Service".
Line 164 still states the Control Plane "revokes its database credentials" when a service is removed, but the warning on lines 170-172 (and the new model in
index.md) explicitly say theconnect_asuser is customer-managed and is not dropped. These two statements contradict each other. Under the new model, removing a service only deletes service instances/containers — it does not modify thedatabase_usersentry.📝 Proposed fix
To remove a service, submit an update request that omits the service from the `services` array. The Control Plane stops and deletes all -service instances for that service and revokes its database credentials. +service instances for that service. The `connect_as` database user is +not modified — it remains in `database_users` and must be removed +separately if no longer needed.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/services/managing.md` around lines 160 - 172, The "Removing a Service" section contradicts the new model: it currently says the Control Plane "revokes its database credentials" but elsewhere (and in index.md) you state the `connect_as` user is customer-managed and not dropped; update the prose in the "Removing a Service" paragraph to remove or replace "revokes its database credentials" with a clear statement that removing a service only stops and deletes service instances and their data directories and does not modify the `database_users` entry or drop the `connect_as` user (mention `connect_as` and `database_users` by name to be explicit).server/internal/orchestrator/swarm/orchestrator.go (1)
412-416:⚠️ Potential issue | 🟠 MajorRestore RAG ServiceUserRole generation on the live RAG path.
ragis dispatched togenerateRAGInstanceResources, so theServiceType == "rag"block ingenerateMCPInstanceResourcesnever runs. As written, RAG no longer emits the svc role resources the PR says should still exist.Suggested direction
Move the RAG role resources into
generateRAGInstanceResourcesbefore serializing the resource list, for example:func (o *Orchestrator) generateRAGInstanceResources(spec *database.ServiceInstanceSpec) (*database.ServiceInstanceResources, error) { ... - orchestratorResources := []resource.Resource{databaseNetwork} + canonicalROID := ServiceUserRoleIdentifier(spec.ServiceSpec.ServiceID, ServiceUserRoleRO) + orchestratorResources := []resource.Resource{ + databaseNetwork, + &ServiceUserRole{ + ServiceID: spec.ServiceSpec.ServiceID, + DatabaseID: spec.DatabaseID, + DatabaseName: spec.DatabaseName, + NodeName: spec.NodeName, + Mode: ServiceUserRoleRO, + }, + } + + for _, nodeInst := range spec.DatabaseNodes { + if nodeInst.NodeName == spec.NodeName { + continue + } + orchestratorResources = append(orchestratorResources, &ServiceUserRole{ + ServiceID: spec.ServiceSpec.ServiceID, + DatabaseID: spec.DatabaseID, + DatabaseName: spec.DatabaseName, + NodeName: nodeInst.NodeName, + Mode: ServiceUserRoleRO, + CredentialSource: &canonicalROID, + }) + }If RAG still requires both RO and RW roles, mirror the previous two-role pattern instead.
Also applies to: 448-453, 663-782
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@server/internal/orchestrator/swarm/orchestrator.go` around lines 412 - 416, The RAG ServiceUserRole creation was removed from the live RAG path because "rag" now routes to generateRAGInstanceResources; restore the svc role generation by moving the RAG-specific ServiceUserRole logic out of generateMCPInstanceResources and into generateRAGInstanceResources (add the role(s) before the final resource list serialization in generateRAGInstanceResources). Locate the RAG-related role creation code patterns in generateMCPInstanceResources (the previous "rag" branch that created ServiceUserRole(s)) and replicate or mirror the two-role (RO/RW) pattern inside generateRAGInstanceResources so RAG still emits the expected svc role resources; ensure you update any helper names used for role creation and keep the roles appended to the same resources slice just prior to returning from generateRAGInstanceResources.docs/services/postgrest.md (1)
217-232:⚠️ Potential issue | 🟡 MinorAlign the JWT example with the grant requirement.
The token claims
role: "app", but the surrounding text says JWT roles must be granted to theconnect_asuser; this example never creates or grants a separateapprole. Use a role the example guarantees is granted, or add explicit role/grant setup.Suggested documentation adjustment
- payload = b64url(json.dumps({"role":"app","exp":int(time.time())+3600})) + payload = b64url(json.dumps({"role":"pgedge_application_read_only","exp":int(time.time())+3600})) ... - curl -X POST http://host-1:3100/products \ - -H "Content-Type: application/json" \ - -H "Authorization: Bearer $TOKEN" \ - --data '{"name": "Widget", "price": 9.99}' + curl http://host-1:3100/products \ + -H "Authorization: Bearer $TOKEN"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/services/postgrest.md` around lines 217 - 232, The JWT example sets payload with role:"app" but the docs require that the role be granted to the connect_as user; either change the payload in the sample (the payload variable used to build TOKEN) to a role that the example actually grants, or add the explicit SQL grant statements for creating/granting the "app" role to the connect_as user before the curl example; update references to payload/header/sig and the TOKEN usage so the example role and the connect_as grant are consistent.
🧹 Nitpick comments (1)
e2e/postgrest_test.go (1)
31-31: Use a non-superuser connect_as fixture.
ConnectAs: "admin"points PostgREST at aSUPERUSER, which can mask missing grants and least-privilege failures in the new authenticator path. Keepadminfor test inspection if needed, but add a separate LOGIN user for PostgREST and update role assertions to target that user.Also applies to: 51-57
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@e2e/postgrest_test.go` at line 31, Replace the PostgREST fixture's superuser connect role by creating and using a dedicated non-superuser login role for PostgREST: change the ConnectAs value from "admin" to the new login user (e.g., "postgrest_user"), add SQL to the test setup to CREATE ROLE postgrest_user LOGIN with only the minimal grants required, keep the existing "admin" role available for inspection only, and update any role assertions (currently asserting behavior for the admin connection) to assert against the new postgrest_user connection and its privileges (symbols: ConnectAs, the test setup/fixture that creates roles, and the assertions that check grants/roles).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@server/internal/orchestrator/swarm/postgrest_authenticator_resource.go`:
- Around line 76-77: The code currently uses ConnectAsUsername in
authenticatorUsername(), which causes revokeStaleAnonRoles and Delete to revoke
grants from a customer-managed connect_as role; change the logic so these
cleanup routines only target grants that this resource created: stop using
ConnectAsUsername as the identifier for revocation and instead either (A) return
the resource-managed authenticator identifier (e.g. a dedicated field like
ManagedAuthenticatorUsername or the original anonymous role name used when
creating grants) from authenticatorUsername(), or (B) add and consult a tracked
state field (e.g. ManagedGrantID/CreatedByThisResource boolean) so
revokeStaleAnonRoles and Delete only revoke the exact grant recorded in state;
update authenticatorUsername(), revokeStaleAnonRoles, and Delete to reference
that managed identifier/state check and never revoke roles based solely on
ConnectAsUsername.
---
Outside diff comments:
In `@docs/services/managing.md`:
- Around line 160-172: The "Removing a Service" section contradicts the new
model: it currently says the Control Plane "revokes its database credentials"
but elsewhere (and in index.md) you state the `connect_as` user is
customer-managed and not dropped; update the prose in the "Removing a Service"
paragraph to remove or replace "revokes its database credentials" with a clear
statement that removing a service only stops and deletes service instances and
their data directories and does not modify the `database_users` entry or drop
the `connect_as` user (mention `connect_as` and `database_users` by name to be
explicit).
In `@docs/services/postgrest.md`:
- Around line 217-232: The JWT example sets payload with role:"app" but the docs
require that the role be granted to the connect_as user; either change the
payload in the sample (the payload variable used to build TOKEN) to a role that
the example actually grants, or add the explicit SQL grant statements for
creating/granting the "app" role to the connect_as user before the curl example;
update references to payload/header/sig and the TOKEN usage so the example role
and the connect_as grant are consistent.
In `@server/internal/orchestrator/swarm/orchestrator.go`:
- Around line 412-416: The RAG ServiceUserRole creation was removed from the
live RAG path because "rag" now routes to generateRAGInstanceResources; restore
the svc role generation by moving the RAG-specific ServiceUserRole logic out of
generateMCPInstanceResources and into generateRAGInstanceResources (add the
role(s) before the final resource list serialization in
generateRAGInstanceResources). Locate the RAG-related role creation code
patterns in generateMCPInstanceResources (the previous "rag" branch that created
ServiceUserRole(s)) and replicate or mirror the two-role (RO/RW) pattern inside
generateRAGInstanceResources so RAG still emits the expected svc role resources;
ensure you update any helper names used for role creation and keep the roles
appended to the same resources slice just prior to returning from
generateRAGInstanceResources.
In `@server/internal/orchestrator/swarm/service_instance.go`:
- Line 38: The struct field ConnectAsUsername has a stale comment limiting it to
RAG, but Refresh() sets s.ConnectAsUsername = desired.ServiceSpec.ConnectAs
unconditionally and the PR extends connect_as usage to mcp and postgrest; update
the comment on ConnectAsUsername in service_instance.go to state that it mirrors
ServiceSpec.ConnectAs for any service type that populates connect_as (e.g., RAG,
MCP, PostgREST) and remove the RAG-only wording, and mention Refresh() as the
place that copies the value.
---
Nitpick comments:
In `@e2e/postgrest_test.go`:
- Line 31: Replace the PostgREST fixture's superuser connect role by creating
and using a dedicated non-superuser login role for PostgREST: change the
ConnectAs value from "admin" to the new login user (e.g., "postgrest_user"), add
SQL to the test setup to CREATE ROLE postgrest_user LOGIN with only the minimal
grants required, keep the existing "admin" role available for inspection only,
and update any role assertions (currently asserting behavior for the admin
connection) to assert against the new postgrest_user connection and its
privileges (symbols: ConnectAs, the test setup/fixture that creates roles, and
the assertions that check grants/roles).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 90865f4d-5dde-4cd7-87c2-0e8083d85d54
📒 Files selected for processing (17)
docs/services/index.mddocs/services/managing.mddocs/services/postgrest.mde2e/postgrest_test.goserver/internal/api/apiv1/validate.goserver/internal/api/apiv1/validate_test.goserver/internal/api/middleware.goserver/internal/database/postgrest_service_config_test.goserver/internal/orchestrator/swarm/orchestrator.goserver/internal/orchestrator/swarm/postgrest_authenticator_resource.goserver/internal/orchestrator/swarm/postgrest_authenticator_resource_test.goserver/internal/orchestrator/swarm/postgrest_config_resource.goserver/internal/orchestrator/swarm/postgrest_config_resource_test.goserver/internal/orchestrator/swarm/postgrest_preflight_resource_test.goserver/internal/orchestrator/swarm/service_instance.goserver/internal/orchestrator/swarm/service_instance_spec.goserver/internal/orchestrator/swarm/service_spec_test.go
| func (r *PostgRESTAuthenticatorResource) authenticatorUsername() string { | ||
| return database.GenerateServiceUsername(r.ServiceID, ServiceUserRoleRW) | ||
| return r.ConnectAsUsername |
There was a problem hiding this comment.
Do not revoke grants from the customer-managed connect_as role.
After authenticatorUsername() switched to ConnectAsUsername, revokeStaleAnonRoles can revoke unrelated customer-managed role memberships, and Delete can remove a pre-existing CONNECT grant. That can break applications sharing the same database user.
Safer cleanup behavior
func (r *PostgRESTAuthenticatorResource) Update(ctx context.Context, rc *resource.Context) error {
- return r.reconcileGrants(ctx, rc)
+ // Idempotently ensure required PostgREST grants without removing
+ // customer-managed memberships from the connect_as role.
+ return r.Create(ctx, rc)
} func (r *PostgRESTAuthenticatorResource) Delete(ctx context.Context, rc *resource.Context) error {
- logger, err := do.Invoke[zerolog.Logger](rc.Injector)
- if err != nil {
- return err
- }
- username := r.authenticatorUsername()
- logger = logger.With().
- Str("service_id", r.ServiceID).
- Str("username", username).
- Logger()
-
- if r.DatabaseName == "" {
- return nil
- }
-
- primary, err := database.GetPrimaryInstance(ctx, rc, r.NodeName)
- if err != nil {
- logger.Warn().Err(err).Msg("failed to get primary instance, skipping REVOKE CONNECT")
- return nil
- }
- conn, err := primary.Connection(ctx, rc, "postgres")
- if err != nil {
- logger.Warn().Err(err).Msg("failed to connect to primary instance, skipping REVOKE CONNECT")
- return nil
- }
- defer conn.Close(ctx)
-
- if _, rErr := conn.Exec(ctx, fmt.Sprintf("REVOKE CONNECT ON DATABASE %s FROM %s",
- sanitizeIdentifier(r.DatabaseName), sanitizeIdentifier(username))); rErr != nil {
- logger.Warn().Err(rErr).Msg("failed to revoke CONNECT privilege, continuing")
- }
+ // connect_as is customer-managed; without tracking exactly which grants
+ // this resource created, deletion must not revoke user privileges.
return nil
}If strict stale-anon cleanup is required, track the exact managed grant in resource state and only revoke that grant when it was created by this resource.
Also applies to: 178-184, 195-224, 228-258
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@server/internal/orchestrator/swarm/postgrest_authenticator_resource.go`
around lines 76 - 77, The code currently uses ConnectAsUsername in
authenticatorUsername(), which causes revokeStaleAnonRoles and Delete to revoke
grants from a customer-managed connect_as role; change the logic so these
cleanup routines only target grants that this resource created: stop using
ConnectAsUsername as the identifier for revocation and instead either (A) return
the resource-managed authenticator identifier (e.g. a dedicated field like
ManagedAuthenticatorUsername or the original anonymous role name used when
creating grants) from authenticatorUsername(), or (B) add and consult a tracked
state field (e.g. ManagedGrantID/CreatedByThisResource boolean) so
revokeStaleAnonRoles and Delete only revoke the exact grant recorded in state;
update authenticatorUsername(), revokeStaleAnonRoles, and Delete to reference
that managed identifier/state check and never revoke roles based solely on
ConnectAsUsername.
Replace auto-created
svc_{service_id}_ro/rwservice user roles for PostgREST with theconnect_asfield — a reference to adatabase_usersentry that the customer owns and manages. This aligns PostgREST with the credential model already used by MCP.validateConnectAswas not being called forpostgrestservice type — fixed: all three service types (mcp, postgrest, rag) now validateconnect_asat the API layer.Added
noDefaultConnectAsflag in validate tests to cover the missing-connect_asand unknown-user error paths for PostgREST.PostgRESTAuthenticatorResource: removedUserRoleIDfield, addedConnectAsUsername;Dependencies()now points toPostgresDatabaseResourceinstead ofServiceUserRole; SQL targets theconnect_asuser directly instead of a generated username.PostgRESTConfigResource: receivesConnectAsUsername/ConnectAsPasswordat construction time from the connect_as database user instead of reading fromServiceUserRole.generateMCPInstanceResources():ServiceUserRolegeneration is now skipped for bothmcpandpostgrest; onlyragstill createssvc_*roles. Per-nodePostgRESTAuthenticatorResourceinstances carryConnectAsUsernamedirectly.ServiceInstanceResource.Dependencies(): removed stale PostgRESTServiceUserRoledeps that blocked container start on the wrong resource.ServiceInstanceSpecResource.Dependencies(): removed PostgRESTServiceUserRoledeps; addedPostgRESTPreflightResourceIdentifier— this ensures the container does not start until the database exists, fixing a race condition where PostgREST failed with "database does not exist" when Patroni had not finished bootstrapping.ServiceInstanceSpecResource.populateCredentials(): simplified — all current service types use config-file credentials, sos.Credentialsis always nil.only 5xx responses use
Error. Eliminates spurious ERR flood from 409 conflict responses during cluster initialization.docs/services/index.md: replacedsvc_*_ro/rwcredential section withconnect_asmodel description.docs/services/managing.md: updatedservice_idfield description, addedconnect_asrow to spec table, addeddatabase_users+connect_asto PostgREST example, updated removal warning.docs/services/postgrest.md: updated overview, all three examples now includedatabase_usersandconnect_as: "app", updated JWT role-claim note to referenceconnect_asuser.e2e/postgrest_test.go: renamedTestPostgRESTServiceUserRoles→TestPostgRESTAuthenticatorRole; assertions now verify NOINHERIT and anon-role grant on theconnect_asuser (admin) instead ofsvc_*roles;TestPostgRESTRemoveverifiesconnect_asuser is NOT dropped;TestPostgRESTMultiHtDBURIchecks authenticator setup instead ofsvc_*role existence; all specs now includeConnectAs: "admin".postgrest_service_config_test.go: updated fixture username fromsvc_pgresttomyappto reflect the connect_as model.New:
postgrest_preflight_resource_test.go,postgrest_authenticator_resource_test.go,postgrest_config_resource_test.go— unit tests for all three PostgREST-specific resource types (37 tests).PLAT-553
Summary
Migrate PostgREST from auto-created
svc_{service_id}_ro/rwserviceuser roles to the
connect_ascredential model — a reference to adatabase_usersentry that the customer owns and manages. This alignsPostgREST with the model already used by MCP and fixes a container
start race condition against Patroni bootstrap.
Changes
Validation
validateConnectAswas not called forpostgrestservice type —fixed; all three service types (mcp, postgrest, rag) now validate
connect_asat the API layernoDefaultConnectAsflag in validate tests to cover missingconnect_asand unknown-user error paths for PostgRESTOrchestrator
PostgRESTAuthenticatorResource: removedUserRoleIDfield, addedConnectAsUsername;Dependencies()now points toPostgresDatabaseResourceinstead ofServiceUserRole; SQL targetsthe
connect_asuser directlyPostgRESTConfigResource: receivesConnectAsUsername/ConnectAsPasswordat construction time from theconnect_asdatabase user instead of reading from
ServiceUserRolegenerateMCPInstanceResources():ServiceUserRolegenerationskipped for both
mcpandpostgrest; onlyragstill createssvc_*roles; per-nodePostgRESTAuthenticatorResourceinstancesnow carry
ConnectAsUsernamedirectlyResource dependencies
ServiceInstanceResource.Dependencies(): removed stale PostgRESTServiceUserRoledeps that were blocking container start on thewrong resource
ServiceInstanceSpecResource.Dependencies(): removed PostgRESTServiceUserRoledeps; addedPostgRESTPreflightResourceIdentifierso the container does not start until the database exists — fixes a
race condition where PostgREST failed with "database does not exist"
when Patroni had not finished bootstrapping
ServiceInstanceSpecResource.populateCredentials(): simplified —all current service types use config-file credentials, so
s.Credentialsis alwaysnilHTTP middleware
Warninstead ofError; only 5xxuses
Error— eliminates spurious ERR flood from 409 conflictresponses during cluster initialization
Documentation
docs/services/index.md: replacedsvc_*_ro/rwcredential sectionwith
connect_asmodel descriptiondocs/services/managing.md: updatedservice_idfield description,added
connect_asrow to spec table, addeddatabase_users+connect_asto PostgREST example, updated removal warning to clarifythe
connect_asuser is never droppeddocs/services/postgrest.md: updated overview prose, all threeexamples now include
database_usersandconnect_as: "app",updated JWT role-claim note to reference
connect_asuserTests
e2e/postgrest_test.go: renamedTestPostgRESTServiceUserRoles→TestPostgRESTAuthenticatorRole; assertions now verify NOINHERIT andanon-role grant on the
connect_asuser (admin) instead ofsvc_*roles;TestPostgRESTRemoveverifiesconnect_asuser isNOT dropped after removal;
TestPostgRESTMultiHostDBURIchecksauthenticator setup instead of
svc_*role count; all specs nowinclude
ConnectAs: "admin"postgrest_service_config_test.go: updated fixture username fromsvc_pgresttomyappto reflect the connect_as modelpostgrest_preflight_resource_test.go,postgrest_authenticator_resource_test.go,postgrest_config_resource_test.go— unit tests for all threePostgREST-specific resource types (37 new tests)
Testing
Checklist