Buckets

Convex component that manages capacity-limited ephemeral groups with open/locked/closed lifecycle for game lobbies, cohorts, and shared sessions.

Installation

npm install @vllnt/convex-buckets

About Buckets

@vllnt/convex-buckets manages temporary groups with a capacity limit and an open, locked, or closed lifecycle. Group members are opaque subject references: your app decides whether they represent people, teams, devices, or other entities.

Use cases
- Game lobbies: admit up to a fixed number of players, then lock the group before a match starts.
- Learning cohorts: group participants into limited-size practice sessions.
- Mobile challenges: form small groups for a shared activity or event.
- Collaborative sessions: reserve a bounded set of participant slots for a workshop or review.

How it works
Mount the component and create a Buckets client. open creates a group; join admits a subject if capacity and state allow it. leave removes a member, lock stops new joins, and close ends the group. Paginated reads expose members without loading the whole group. Cleanup operates in bounded batches.

Integration
Your backend owns admission policy, authentication, authorization, and any discovery or scheduling UI. Separate mounts isolate data; scopes namespace groups. Buckets stores membership and capacity, not network connections, presence, or skill-based matchmaking.

Maintained by [bntvllnt](https://bntvllnt.com) at [VLLNT](https://vllnt.com). Find the author on [GitHub](https://github.com/bntvllnt) and [X](https://x.com/bntvllnt).

Benefits

Use cases

how to implement a game lobby with player capacity limit in Convex

convex-buckets provides a Buckets client with open, join, lock, and close mutations. Call open with a capacity option to create the lobby, join to admit players up to that limit, and lock to stop new joins before a match starts. The component enforces capacity via OCC on a memberCount field so concurrent join mutations retry rather than overfill the lobby.

how to group users into fixed-size cohorts or sessions in Convex

Use convex-buckets to open a bucket with a specific capacity and join opaque subjectRef strings representing users, teams, or devices. The component does not interpret subject references, so your backend controls what entity each ref represents and handles any admission policy or authorization before calling join.

how to paginate group members in a Convex component

convex-buckets exposes a paginateMembers query that accepts standard Convex paginationOpts and returns a page, isDone flag, and continueCursor. For bounded previews, listMembers returns up to 500 members in a single query without requiring cursor handling.

how to clean up ephemeral groups and remove member data in Convex

convex-buckets provides eraseBucket and eraseSubject mutations. eraseBucket closes the bucket and removes members in fenced scheduled batches keyed to the original document ID so stale scheduled work cannot erase a replacement bucket. eraseSubject deletes one bounded batch per call and schedules no continuation, letting the caller control drain timing.

Frequently asked questions

What is a subjectRef in convex-buckets?

A subjectRef is an opaque string your application provides to identify a group member. convex-buckets does not interpret or validate the value, so it can represent a user ID, team ID, device ID, or any other entity your backend defines. Authorization and identity resolution remain the responsibility of your own Convex mutations before they call join.

How does convex-buckets prevent a group from exceeding its capacity when multiple users join at the same time?

convex-buckets stores memberCount directly on the bucket document and uses Convex optimistic concurrency control to patch it atomically. If two join mutations conflict, Convex retries the losing transaction, so the final count cannot exceed the capacity set when the bucket was opened.

Can I run multiple independent sets of groups in the same Convex app using convex-buckets?

Yes. Mount the component multiple times with distinct names using app.use(bucketsConfig, { name: 'first' }) and app.use(bucketsConfig, { name: 'second' }), then create a separate Buckets client for each mount. Data between mounts is fully isolated at the table level. Within a single mount, scopes can further partition groups without requiring a separate mount.

Does convex-buckets handle authentication or authorization?

No. convex-buckets is auth-agnostic and does not enforce any access control. Your Convex mutations must authenticate the caller, authorize the action, and derive the correct scope and subjectRef before calling the component's join, leave, lock, or close methods. Scopes are namespaces for partitioning data, not security boundaries.

What happens when a bucket is locked versus closed in convex-buckets?

A locked bucket rejects new joins but still allows existing members to leave. A closed bucket is terminal and does not allow joins or leaves. The lifecycle progresses in one direction: open accepts joins and leaves, locked stops joins but allows leaves, and closed ends the group. The join result includes a reason field with values like locked, closed, full, or already_member when a join is rejected.

Links