Creating a workspace for an organization without a default pool creates one with no plan #240

Open
opened 2026-10-18 19:36:17 +00:00 by cgalo5758 · 0 comments
Owner

What happens

An organization's default pool is the pool its plan and entitlements are delivered to. Signup and the member's "Create workspace" form share one function, CreateWorkspaceWithPrimaryAssignment, and when the organization has no default pool, that function creates one.

At signup this is the normal path: AutoProvision opens its transaction with BeginMaterializing, which takes the plan lock, and gives the new pool its default plan right after. The "Create workspace" handler calls the same function in a plain transaction, with neither. If an organization has no default pool when a member creates a workspace, the handler creates one with no plan, and the pool stays plan-less until something else restores its default.

What should happen

Only signup creates an organization's default pool, under the plan lock and followed by its default plan. The shared function takes the pool as signup created it, and the "Create workspace" path refuses with an error and logs it at Error level when the pool is missing.

Where

internal/provisioning/workspace.go:72-86, internal/server/workspace_partials.go:121, internal/provisioning/provisioning.go:47 and :125

Steps

An organization has no default pool only through damaged data: signup creates it, and nothing deletes one. The "Create workspace" control is shown only when the organization already has more than one workspace (#139), but the route answers a direct request from any signed-in member.

Why it matters

A pool with no plan delivers no entitlements, and creating it silently covers up the damaged data that left the organization without one instead of reporting it.

Related: #221 and #186 list this handler's transaction among those opened by hand; neither covers which code may create a default pool.

### What happens An organization's default pool is the pool its plan and entitlements are delivered to. Signup and the member's "Create workspace" form share one function, `CreateWorkspaceWithPrimaryAssignment`, and when the organization has no default pool, that function creates one. At signup this is the normal path: `AutoProvision` opens its transaction with `BeginMaterializing`, which takes the plan lock, and gives the new pool its default plan right after. The "Create workspace" handler calls the same function in a plain transaction, with neither. If an organization has no default pool when a member creates a workspace, the handler creates one with no plan, and the pool stays plan-less until something else restores its default. ### What should happen Only signup creates an organization's default pool, under the plan lock and followed by its default plan. The shared function takes the pool as signup created it, and the "Create workspace" path refuses with an error and logs it at Error level when the pool is missing. ### Where `internal/provisioning/workspace.go:72-86`, `internal/server/workspace_partials.go:121`, `internal/provisioning/provisioning.go:47` and `:125` ### Steps An organization has no default pool only through damaged data: signup creates it, and nothing deletes one. The "Create workspace" control is shown only when the organization already has more than one workspace (#139), but the route answers a direct request from any signed-in member. ### Why it matters A pool with no plan delivers no entitlements, and creating it silently covers up the damaged data that left the organization without one instead of reporting it. Related: #221 and #186 list this handler's transaction among those opened by hand; neither covers which code may create a default pool.
cgalo5758 added the
kind
bug
area/identityarea/entitlements
priority
low
labels 2026-10-18 19:36:17 +00:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: wiki-cafe/member-console#240