Version: 1.0 Status: Specification Author: DBBasic Project Date: November 2025
Links: - PyPI: https://pypi.org/project/dbbasic-groups/ (not yet published) - GitHub: https://github.com/askrobots/dbbasic-groups (not yet published) - Specification: http://dbbasic.com/groups-spec
"Groups are just shared context with access control."
The critical insight: Most group systems over-complicate what is fundamentally simple: a named container that some users can access and some can't. Everything else (posts, discussions, files) belongs to other modules.
- Posts, comments, reactions, polls, events, files, photos
- Albums, live videos, shops, fundraisers, badges, units
- Member questions, group rules, learning units, member bio
- 50+ different features
Result: Feature bloat, confusing UX, slow performance.
- Just a list of emails
- No web interface
- No roles or permissions
Result: No visibility, no moderation tools, spam problems.
Group = {
name: "Django Developers",
description: "...",
members: [u001, u002, u003],
privacy: "public"
}
# Then integrate:
- Forum posts scoped to group
- Jobs posted to group
- Files shared with group
- Events for group members
Why this works: - Single responsibility (access control) - Composable (works with other modules) - Simple data model - No feature duplication
id name slug description privacy created_by created_at updated_at member_count
Fields:
- id: Unique group ID (e.g., "g001", "g002")
- name: Group name (e.g., "Django Developers")
- slug: URL-friendly name (e.g., "django-developers")
- description: Group description (markdown supported)
- privacy: Group privacy (public, private, secret)
- created_by: User who created the group
- created_at: When group was created
- updated_at: When group was last modified
- member_count: Cached count of members
id name slug description privacy created_by created_at updated_at member_count
g001 Django Developers django-developers Community for Django web developers public u042 2025-10-01T10:00:00 2025-11-01T15:00:00 342
g002 Python Freelancers python-freelancers Freelance Python developers private u055 2025-10-05T14:00:00 2025-11-01T10:00:00 128
g003 Client Network client-network Vetted clients only secret u088 2025-10-10T09:00:00 2025-10-15T12:00:00 45
id group_id user_id role status joined_at updated_at
Fields:
- id: Unique membership ID (e.g., "m001", "m002")
- group_id: Group being joined
- user_id: User joining
- role: Member role (owner, admin, member)
- status: Membership status (active, pending, banned, left)
- joined_at: When user joined
- updated_at: When membership was last modified
id group_id user_id role status joined_at updated_at
m001 g001 u042 owner active 2025-10-01T10:00:00 2025-10-01T10:00:00
m002 g001 u055 admin active 2025-10-01T11:00:00 2025-10-01T11:00:00
m003 g001 u077 member active 2025-10-02T09:00:00 2025-10-02T09:00:00
m004 g001 u099 member pending 2025-11-01T14:00:00 2025-11-01T14:00:00
m005 g002 u055 owner active 2025-10-05T14:00:00 2025-10-05T14:00:00
m006 g002 u077 member banned 2025-10-10T10:00:00 2025-10-15T16:00:00
from dbbasic_groups import Groups
# Initialize
groups = Groups('/path/to/data')
# Create group
group_id = groups.create(
name='Django Developers',
slug='django-developers',
description='Community for Django web developers',
privacy='public',
created_by='u042'
)
# Get group
group = groups.get('g001')
# Get group by slug
group = groups.get_by_slug('django-developers')
# List groups
all_groups = groups.list()
public_groups = groups.list(privacy='public')
my_groups = groups.list_by_user('u042')
# Update group
groups.update('g001', description='Updated description')
# Delete group
groups.delete('g001')
# Join group
groups.join(group_id='g001', user_id='u077')
# Leave group
groups.leave(group_id='g001', user_id='u077')
# Approve membership (for private groups)
groups.approve_membership(membership_id='m004')
# Reject membership
groups.reject_membership(membership_id='m004')
# Ban member
groups.ban_member(group_id='g001', user_id='u077')
# Get members
members = groups.get_members('g001')
# Get member role
role = groups.get_role(group_id='g001', user_id='u042')
# Check membership
is_member = groups.is_member(group_id='g001', user_id='u077')
# Promote to admin
groups.promote(group_id='g001', user_id='u055', role='admin')
# Demote to member
groups.demote(group_id='g001', user_id='u055', role='member')
groups.create(..., privacy='public')
Characteristics: - Anyone can see group - Anyone can see members - Anyone can see content - Anyone can join (no approval needed)
Use cases: Open communities, public discussions
groups.create(..., privacy='private')
Characteristics: - Anyone can see group exists - Only members can see member list - Only members can see content - Join requires approval
Use cases: Professional networks, curated communities
groups.create(..., privacy='secret')
Characteristics: - Only members can see group exists - Only members can see member list - Only members can see content - Join by invitation only
Use cases: Private clients, inner circles
┌──────────┐
│ PENDING │ (for private groups)
└────┬─────┘
│
├─────→ ACTIVE (approved by admin)
│
└─────→ REJECTED (declined by admin)
┌──────────┐
│ ACTIVE │ (member is in group)
└────┬─────┘
│
├─────→ LEFT (member left voluntarily)
│
└─────→ BANNED (removed by admin/owner)
# api/groups/create.py
from dbbasic_groups import Groups
from dbbasic_web.responses import json_response
def handle(request):
groups = Groups('/data')
group_id = groups.create(
name=request.form['name'],
slug=request.form['slug'],
description=request.form['description'],
privacy=request.form['privacy'],
created_by=request.user['id']
)
return json_response({'group_id': group_id})
# api/groups/join.py
def handle(request):
groups = Groups('/data')
groups.join(
group_id=request.form['group_id'],
user_id=request.user['id']
)
return json_response({'success': True})
# api/groups/[slug].py
def handle(request, slug):
groups = Groups('/data')
group = groups.get_by_slug(slug)
# Check access
if group['privacy'] == 'secret':
if not groups.is_member(group['id'], request.user['id']):
return json_response({'error': 'Not found'}, status=404)
members = groups.get_members(group['id'])
return json_response({
'group': group,
'members': members
})
<!-- Group card -->
<div class="group-card">
<h3>{{ group.name }}</h3>
<div class="group-meta">
<span class="privacy">{{ group.privacy }}</span>
<span class="members">{{ group.member_count }} members</span>
</div>
<p>{{ group.description | truncate(200) }}</p>
<a href="/groups/{{ group.slug }}" class="btn">View Group</a>
</div>
<!-- Join button -->
{% if not is_member %}
<form method="POST" action="/api/groups/join">
<input type="hidden" name="group_id" value="{{ group.id }}">
<button type="submit">
{% if group.privacy == 'private' %}
Request to Join
{% else %}
Join Group
{% endif %}
</button>
</form>
{% endif %}
# All groups
dbbasic groups list
# By privacy
dbbasic groups list --privacy public
# Groups for user
dbbasic groups list --user u042
dbbasic groups create \
--name "Django Developers" \
--slug django-developers \
--description "Community for Django developers" \
--privacy public \
--creator u042
dbbasic groups members g001
dbbasic groups join g001 --user u077
dbbasic groups ban g001 --user u077
Auto-discovered by dbbasic-admin:
# Automatic CRUD interface at /admin/groups/
# Shows:
# - Recent groups
# - Filter by privacy
# - Member count analytics
# - Pending approvals
# - Ban management
Scope forum threads to groups:
from dbbasic_groups import Groups
from dbbasic_forum import Forum
groups = Groups('/data')
forum = Forum('/data')
# Create group-specific forum thread
thread_id = forum.create_thread(
title='Django 5.0 Discussion',
content='...',
author_id='u042',
group_id='g001' # Only group members can see
)
# Get threads for group
group_threads = forum.get_threads(group_id='g001')
Post jobs to specific groups:
from dbbasic_groups import Groups
from dbbasic_jobs import Jobs
groups = Groups('/data')
jobs = Jobs('/data')
# Create job for group
job_id = jobs.create(
title='Django Developer Needed',
description='...',
client_id='u042',
group_id='g001' # Only group members see this job
)
# Get jobs for group
group_jobs = jobs.list(group_id='g001')
Auto-follow group members:
from dbbasic_groups import Groups
from dbbasic_follows import Follows
groups = Groups('/data')
follows = Follows('/data')
# When user joins group
groups.join(group_id='g001', user_id='u077')
# Suggest following other members
members = groups.get_members('g001')
for member in members:
if member['user_id'] != 'u077':
# Suggest following
follows.suggest(from_user='u077', to_user=member['user_id'])
# Search groups by name
results = groups.search('Django')
# All public groups
public = groups.list(privacy='public')
# Groups user belongs to
my_groups = groups.list_by_user('u042')
# Groups user owns
owned = groups.list_by_user('u042', role='owner')
Add categories for better organization:
groups.create(
...,
category='technology'
)
# List by category
tech_groups = groups.list(category='technology')
Add tags for discovery:
groups.create(
...,
tags=['python', 'django', 'web']
)
# Search by tag
python_groups = groups.search_by_tag('python')
Limit group size:
groups.create(
...,
max_members=100
)
# Check if group is full
if group['member_count'] >= group['max_members']:
raise ValueError('Group is full')
Invite users to private/secret groups:
# Send invitation
groups.invite(
group_id='g001',
from_user='u042',
to_user='u077'
)
# Accept invitation
groups.accept_invitation(invitation_id='inv001')
Target: ~250 lines of Python
dbbasic_groups/
├── __init__.py # 10 lines
├── groups.py # 120 lines (CRUD, search)
├── memberships.py # 80 lines (join, leave, roles)
├── permissions.py # 30 lines (access control)
└── cli.py # 10 lines (command-line interface)
Total: ~250 lines
Secret groups should be invisible to non-members:
def get_by_slug(self, slug, requesting_user_id=None):
group = self.db.get_by_slug(slug)
if group['privacy'] == 'secret':
if not self.is_member(group['id'], requesting_user_id):
raise NotFoundError('Group not found')
return group
Only owner/admin can manage group:
def ban_member(self, group_id, user_id, requesting_user_id):
role = self.get_role(group_id, requesting_user_id)
if role not in ['owner', 'admin']:
raise PermissionError('Only owner/admin can ban members')
# ...
Owner cannot be removed:
def remove_member(self, group_id, user_id):
membership = self.get_membership(group_id, user_id)
if membership['role'] == 'owner':
raise ValueError('Cannot remove owner')
# ...
# Find all public groups
grep "public" data/groups.tsv
# Find groups created by user u042
grep "u042" data/groups.tsv
# Find active memberships for group g001
grep "g001\t.*\tactive" data/memberships.tsv
# Find all admins
grep "admin" data/memberships.tsv
Groups only handle access control. They don't: - Store posts (use dbbasic-forum) - Store files (use dbbasic-upload) - Store events (use dbbasic-events)
Why? Separation of concerns, composability, no duplication.
Three levels (public, private, secret) cover all use cases:
Public: Open communities (Python user group)
Private: Professional networks (consulting collective)
Secret: Inner circles (private client group)
Only 3 roles: owner, admin, member
Most group systems have 10+ roles that confuse users: - "Moderator" - "Contributor" - "Viewer" - "Editor" - etc.
We skip all that. Either you can manage the group (owner/admin) or you can't (member).
Four statuses cover the lifecycle:
- pending: Waiting approval
- active: Current member
- left: Departed voluntarily
- banned: Removed by admin
Clean state machine, no ambiguity.
| Feature | Facebook Groups | LinkedIn Groups | Slack | dbbasic-groups |
|---|---|---|---|---|
| Lines of Code | ~500K | ~500K | ~500K | ~250 |
| Privacy Levels | 3 | 2 | 3 | 3 |
| Member Roles | 5+ | 3 | 7+ | 3 |
| Posts/Discussions | Built-in | Built-in | Built-in | Use dbbasic-forum |
| File Storage | Built-in | Built-in | Built-in | Use dbbasic-upload |
| TSV Storage | No | No | No | Yes |
| grep-able | No | No | No | Yes |
# Create private group for vetted freelancers
group_id = groups.create(
name='Python Freelancers',
privacy='private',
created_by='u042'
)
# Members apply to join
groups.join(group_id, 'u055') # Status: pending
# Owner approves
groups.approve_membership(membership_id)
# Post jobs to group only
jobs.create(..., group_id=group_id)
# Create public Django community
group_id = groups.create(
name='Django Developers',
privacy='public',
created_by='u042'
)
# Anyone can join instantly
groups.join(group_id, 'u077') # Status: active (no approval)
# Group-specific forum
forum.create_thread(..., group_id=group_id)
# Create secret group for high-value clients
group_id = groups.create(
name='Elite Clients',
privacy='secret',
created_by='u042'
)
# Only visible to members
# Invite-only access
groups.invite(group_id, from_user='u042', to_user='u055')
Version: 1.0 Specification Implementation: Not yet started Expected Size: ~250 lines Dependencies: dbbasic-tsv only
Optional Integrations: - dbbasic-forum (group discussions) - dbbasic-jobs (group job board) - dbbasic-follows (member connections) - dbbasic-upload (group files)
Last Updated: November 2025