KS
Killer-Skills

tenancy-enforcer — how to use tenancy-enforcer how to use tenancy-enforcer, tenancy-enforcer setup guide, MongoDB query security, multi-tenant community platform, tenancy-enforcer alternative, tenancy-enforcer vs competitor, implementing tenancy-enforcer for AI agents, tenancy-enforcer install, secure MongoDB queries

v1.0.0
GitHub

About this Skill

Perfect for Multi-Tenancy Agents needing secure MongoDB query management. tenancy-enforcer is a multi-tenant community platform enforcing secure MongoDB queries with communityId filtering

Features

Enforces communityId filtering for MongoDB queries (find, update, delete)
Supports repository method creation with communityId inclusion
Implements service layer logic for role-scoped notifications
Adds new indexes to schemas for efficient query execution
Creates aggregate pipelines for complex data processing

# Core Topics

muhammadcaeed muhammadcaeed
[0]
[0]
Updated: 3/4/2026

Quality Score

Top 5%
33
Excellent
Based on code quality & docs
Installation
SYS Universal Install (Auto-Detect)
Cursor IDE Windsurf IDE VS Code IDE
> npx killer-skills add muhammadcaeed/aegis/tenancy-enforcer

Agent Capability Analysis

The tenancy-enforcer MCP Server by muhammadcaeed is an open-source Categories.community integration for Claude and other AI agents, enabling seamless task automation and capability expansion. Optimized for how to use tenancy-enforcer, tenancy-enforcer setup guide, MongoDB query security.

Ideal Agent Persona

Perfect for Multi-Tenancy Agents needing secure MongoDB query management.

Core Value

Empowers agents to enforce guard duty visibility and household payments using secure MongoDB queries, ensuring every database query includes `communityId` in the filter, with support for JWT token extraction and repository method creation.

Capabilities Granted for tenancy-enforcer MCP Server

Enforcing community-based access control for MongoDB queries
Generating secure repository methods with communityId filtering
Implementing service layer logic with cross-community access protection

! Prerequisites & Limits

  • Requires MongoDB database connection
  • Must include `communityId` in every database query filter
  • Cross-community access is strictly prohibited
Project
SKILL.md
6.3 KB
.cursorrules
1.2 KB
package.json
240 B
Ready
UTF-8

# Tags

[No tags]
SKILL.md
Readonly

Aegis Multi-Tenancy Enforcer

When This Skill Applies

  • Writing ANY MongoDB query (find, update, delete)
  • Creating repository methods
  • Implementing service layer logic
  • Adding new indexes to schemas
  • Creating aggregate pipelines

The Absolute Rule

Every database query MUST include communityId in the filter.

No exceptions. Cross-community access is a security breach.

Source of communityId

Correct: From JWT Token

typescript
1// Controller extracts from token 2@Get() 3async findAll(@CurrentUser() user: JwtPayload) { 4 return this.service.findAll(user.communityId); 5} 6 7// Service passes to repository 8async findAll(communityId: string) { 9 return this.repository.findAll(communityId); 10} 11 12// Repository includes in query 13async findAll(communityId: string) { 14 return this.model.find({ 15 communityId: new Types.ObjectId(communityId), 16 }); 17}

Wrong: From Request Body

typescript
1// NEVER DO THIS 2@Post() 3async create(@Body() dto: CreateDto) { 4 // dto.communityId could be forged by attacker 5 return this.service.create(dto.communityId, dto); 6}

Repository Method Patterns

Find Methods

typescript
1// Always require communityId as parameter 2async findById(id: string, communityId: string): Promise<Document | null> { 3 return this.model.findOne({ 4 _id: new Types.ObjectId(id), 5 communityId: new Types.ObjectId(communityId), // REQUIRED 6 }); 7} 8 9async findByHousehold(householdId: string, communityId: string): Promise<Document[]> { 10 return this.model.find({ 11 householdId: new Types.ObjectId(householdId), 12 communityId: new Types.ObjectId(communityId), // REQUIRED 13 }); 14}

Update Methods

typescript
1async updateById( 2 id: string, 3 communityId: string, 4 data: UpdateDto, 5): Promise<Document | null> { 6 return this.model.findOneAndUpdate( 7 { 8 _id: new Types.ObjectId(id), 9 communityId: new Types.ObjectId(communityId), // REQUIRED in filter 10 }, 11 { $set: data }, 12 { new: true }, 13 ); 14}

Delete Methods

typescript
1async deleteById(id: string, communityId: string): Promise<boolean> { 2 const result = await this.model.deleteOne({ 3 _id: new Types.ObjectId(id), 4 communityId: new Types.ObjectId(communityId), // REQUIRED 5 }); 6 return result.deletedCount > 0; 7}

Common Anti-Patterns (FORBIDDEN)

Global Queries

typescript
1// WRONG - No communityId filter 2async findAll(): Promise<Document[]> { 3 return this.model.find({}); // SECURITY BREACH 4} 5 6// WRONG - ID-only lookup 7async findById(id: string): Promise<Document | null> { 8 return this.model.findById(id); // Can access ANY community's data 9}

Optional communityId

typescript
1// WRONG - communityId should never be optional 2async findById(id: string, communityId?: string): Promise<Document | null> { 3 const filter: any = { _id: new Types.ObjectId(id) }; 4 if (communityId) { 5 filter.communityId = new Types.ObjectId(communityId); 6 } 7 return this.model.findOne(filter); // Dangerous when omitted 8}

Trusting Request Body

typescript
1// WRONG - Request body can be forged 2@Post() 3async create(@Body() dto: CreatePaymentDto) { 4 return this.paymentService.create({ 5 ...dto, 6 communityId: dto.communityId, // Attacker can set any communityId 7 }); 8} 9 10// CORRECT - Use JWT context 11@Post() 12async create( 13 @Body() dto: CreatePaymentDto, 14 @CurrentUser() user: JwtPayload, 15) { 16 return this.paymentService.create({ 17 ...dto, 18 communityId: user.communityId, // From authenticated token 19 }); 20}

Index Patterns

All compound indexes MUST have communityId as the first field for query efficiency and logical isolation.

typescript
1// CORRECT - communityId first 2@Schema() 3export class Payment { 4 // ... 5} 6 7PaymentSchema.index({ communityId: 1, householdId: 1 }); 8PaymentSchema.index({ communityId: 1, state: 1 }); 9PaymentSchema.index({ communityId: 1, householdId: 1, billingPeriodStart: 1 }, { unique: true }); 10 11// WRONG - communityId not first 12PaymentSchema.index({ householdId: 1, communityId: 1 }); // Query won't use index efficiently 13PaymentSchema.index({ state: 1 }); // Global index, no tenant isolation

Aggregate Pipeline Rules

typescript
1// CORRECT - $match with communityId as first stage 2async aggregateByHousehold(communityId: string) { 3 return this.model.aggregate([ 4 { 5 $match: { 6 communityId: new Types.ObjectId(communityId), // FIRST stage 7 }, 8 }, 9 { 10 $group: { 11 _id: '$householdId', 12 total: { $sum: '$amount' }, 13 }, 14 }, 15 ]); 16} 17 18// WRONG - Missing communityId or not first 19async aggregateAll() { 20 return this.model.aggregate([ 21 { $group: { _id: '$householdId', total: { $sum: '$amount' } } }, // No tenant filter 22 ]); 23}

Service Layer Enforcement

Services should always require communityId from controllers, never have default values or lookups.

typescript
1// CORRECT - Explicit communityId parameter 2@Injectable() 3export class PaymentService { 4 async findByHousehold(householdId: string, communityId: string) { 5 return this.repository.findByHousehold(householdId, communityId); 6 } 7} 8 9// WRONG - Fetching communityId from related entity 10@Injectable() 11export class PaymentService { 12 async findByHousehold(householdId: string) { 13 const household = await this.householdService.findById(householdId); 14 // What if household doesn't exist? What if wrong community? 15 return this.repository.findByHousehold(householdId, household.communityId); 16 } 17}

Exception: Community Collection

The Community collection itself does not have a communityId field - it IS the tenant root. When querying communities:

typescript
1// Community queries use _id directly (admin operations only) 2async findCommunityById(id: string): Promise<Community | null> { 3 return this.communityModel.findById(id); 4}

Verification Checklist

Before committing any repository/service code:

  • Every find() call includes communityId in filter
  • Every findOne() call includes communityId in filter
  • Every updateOne()/updateMany() includes communityId in filter
  • Every deleteOne()/deleteMany() includes communityId in filter
  • All aggregate pipelines have $match with communityId as first stage
  • communityId comes from JWT/controller, not request body
  • No methods have optional communityId parameter
  • New indexes have communityId as first field in compound indexes

Related Skills

Looking for an alternative to tenancy-enforcer or building a Categories.community AI Agent? Explore these related open-source MCP Servers.

View All

widget-generator

Logo of f
f

widget-generator is an open-source AI agent skill for creating widget plugins that are injected into prompt feeds on prompts.chat. It supports two rendering modes: standard prompt widgets using default PromptCard styling and custom render widgets built as full React components.

149.6k
0
Design

chat-sdk

Logo of lobehub
lobehub

chat-sdk is a unified TypeScript SDK for building chat bots across multiple platforms, providing a single interface for deploying bot logic.

73.0k
0
Communication

zustand

Logo of lobehub
lobehub

The ultimate space for work and life — to find, build, and collaborate with agent teammates that grow with you. We are taking agent harness to the next level — enabling multi-agent collaboration, effortless agent team design, and introducing agents as the unit of work interaction.

72.8k
0
Communication

data-fetching

Logo of lobehub
lobehub

The ultimate space for work and life — to find, build, and collaborate with agent teammates that grow with you. We are taking agent harness to the next level — enabling multi-agent collaboration, effortless agent team design, and introducing agents as the unit of work interaction.

72.8k
0
Communication