The gate rootless Docker was under, which I did not know about when the Postgres role
replaced it. It inherits the rule along with the purpose: a member not trusted to run
containers is not thereby trusted to run databases.
rolePermitsDatabase() is the single place that rule is written. Admin is deliberately
NOT included — administering the platform is not developing on it, and they are
separate roles precisely so they can be held separately. Say so if that is wrong.
provisionOsAccount now takes the role. Two call sites: create-user passes what the
owner picked, provision-linux-route reads it from the row — which makes that route
the way a member promoted to Developer gets the database role they did not qualify
for when their account was made.
The half that makes the gate real is in updateUserRoleHandler. Without it the rule
would decide what a Developer gets at creation and never look again, so demoting one
would leave their role, their databases and a working password in their ~/.zshenv —
a permission surviving its own revocation, with the UI then saying something untrue.
Revoke before recording, grant after: the drop runs BEFORE updateUser so a failure
aborts with the role unchanged and the whole thing retryable. Promotion runs after and
is non-fatal, like every other provisioning step.
Demotion keeps their data, same as deletion — databases are reassigned to the platform
role, not dropped — and logs where it went, so it does not look deleted.
KNOWN, not handled: a demoted member keeps a stale ~/.pgpass and ~/.zshenv naming a
role that no longer exists. Harmless (the connection just fails) but untidy, and it
means `cat ~/.zshenv` shows a password that no longer works.
Verified: transpiles, both call sites updated. Still no tsgo.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>