Skip to content

initial commit of cagematch - #311

Open
jasonmorais wants to merge 6 commits into
mainfrom
jason/rate-limit-cagematch
Open

initial commit of cagematch#311
jasonmorais wants to merge 6 commits into
mainfrom
jason/rate-limit-cagematch

Conversation

@jasonmorais

@jasonmorais jasonmorais commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Summary by Sourcery

Introduce a backend-neutral application rate-limiting layer with Redis and MongoDB implementations, wire it into the API/community creation flow, and add Redis local-dev and test infrastructure.

New Features:

  • Add @cagematch/rate-limiting core package defining policies, subjects, and a shared ServiceRateLimiting facade.
  • Provide Redis- and MongoDB-backed rate-limiting services and a Redis memory-server mock for local development and tests.
  • Integrate rate limiting into the OCOM API context and community creation application service, with actor-aware policies for community.create.

Enhancements:

  • Extend local-dev tooling and OCOM local-dev config to support worktree-aware Redis ports and REDIS_URL conversion.
  • Register a MongoDB rate-limit model with TTL index in the Mongoose data model context.
  • Document the Redis-backed rate-limiting architecture decision and new packages.

Build:

  • Register new cagematch packages in the workspace, add Redis dependencies and memory-server allowBuild entries, and update a few dependency overrides.

Tests:

  • Add unit and integration tests for rate-limiting core logic, Redis and Mongo backends, and API bootstrap wiring, including actor-specific policy selection.
  • Extend E2E infrastructure to start a Redis mock server alongside existing services for verification suites.

@sourcery-ai

sourcery-ai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Introduce a backend-neutral feature rate limiting core (Mongo + Redis contenders), wire it into the OCOM API/community creation flow, and extend local dev + E2E tooling to run a Redis memory server with deterministic worktree ports and REDIS_URL conversion.

Sequence diagram for community.create rate limiting flow

sequenceDiagram
  actor User
  participant ApiBootstrap as ApiBootstrap
  participant ApplicationServicesFactory as buildApplicationServicesFactory
  participant CommunityService as Community.create
  participant RateLimiter as ServiceRateLimiting

  User->>ApiBootstrap: HTTP request (community.create)
  ApiBootstrap->>ApiBootstrap: startUp ServiceRedisRateLimiting
  ApiBootstrap->>ApplicationServicesFactory: buildApplicationServicesFactory(context)
  ApplicationServicesFactory->>ApplicationServicesFactory: derive RateLimitSubject
  ApplicationServicesFactory->>CommunityService: Community.create(command, rateLimitingService, rateLimitSubject)
  CommunityService->>RateLimiter: consume({ feature: "community.create", subject: rateLimitSubject })
  alt [decision.allowed]
    RateLimiter-->>CommunityService: RateLimitDecision { allowed: true, remaining }
    CommunityService->>CommunityService: perform community creation
  else [!decision.allowed]
    RateLimiter-->>CommunityService: RateLimitDecision { allowed: false, retryAfterMs }
    CommunityService-->>User: Error("Rate limit exceeded for feature community.create")
  end
Loading

File-Level Changes

Change Details Files
Add a backend-neutral rate limiting core plus MongoDB and Redis service implementations.
  • Define RateLimit* types and ServiceRateLimiting facade with policy resolution and fixed-window counter orchestration.
  • Implement Mongo-backed ServiceMongoRateLimiting using atomic findOneAndUpdate, TTL-based cleanup, and a dedicated client from COSMOSDB_CONNECTION_STRING.
  • Implement Redis-backed ServiceRedisRateLimiting with a Lua script-based consumeRateLimit command, typed script wrapper, and dedicated client driven by REDIS_URL.
  • Add integration and unit tests for core policy resolution, key construction, and Mongo/Redis contenders against real in-memory servers.
  • Document responsibilities and boundaries of core and contender packages via README and manifest files.
packages/cagematch/rate-limiting/src/rate-limiting.ts
packages/cagematch/rate-limiting/src/index.ts
packages/cagematch/rate-limiting/package.json
packages/cagematch/rate-limiting/README.md
packages/cagematch/rate-limiting/manifest.md
packages/cagematch/rate-limiting/vitest.config.ts
packages/cagematch/rate-limiting/src/index.test.ts
packages/cagematch/rate-limiting/tsconfig.json
packages/cagematch/rate-limiting-mongo/src/mongo-rate-limiting.ts
packages/cagematch/rate-limiting-mongo/src/index.ts
packages/cagematch/rate-limiting-mongo/src/index.test.ts
packages/cagematch/rate-limiting-mongo/src/mongo-rate-limiting.integration.test.ts
packages/cagematch/rate-limiting-mongo/package.json
packages/cagematch/rate-limiting-mongo/README.md
packages/cagematch/rate-limiting-mongo/manifest.md
packages/cagematch/rate-limiting-mongo/tsconfig.json
packages/cagematch/rate-limiting-mongo/vitest.config.ts
packages/cagematch/rate-limiting-redis/src/redis-rate-limiting.ts
packages/cagematch/rate-limiting-redis/src/consume-rate-limit.script.ts
packages/cagematch/rate-limiting-redis/src/index.ts
packages/cagematch/rate-limiting-redis/src/index.test.ts
packages/cagematch/rate-limiting-redis/src/redis-rate-limiting.integration.test.ts
packages/cagematch/rate-limiting-redis/package.json
packages/cagematch/rate-limiting-redis/README.md
packages/cagematch/rate-limiting-redis/manifest.md
packages/cagematch/rate-limiting-redis/tsconfig.json
packages/cagematch/rate-limiting-redis/vitest.config.ts
Wire rate limiting into the API bootstrap and OCOM application-services context, including community.create enforcement.
  • Register a shared ServiceMongoose instance plus a RateLimitingService in apps/api infrastructure, selecting Redis vs Mongo via configuration.
  • Expose rateLimitingService on ApiContextSpec and propagate it through buildApplicationServicesFactory into Community application services.
  • Derive RateLimitSubject from JWT verification (actorType, staff role, optional community scope) alongside existing passport construction.
  • Inject RateLimitingService and RateLimitSubject into Community context and use it in community.create to short-circuit when limits are exceeded.
  • Add tests asserting bootstrap registers ServiceRedisRateLimiting when REDIS_URL is present and that community.create enforces limits.
apps/api/src/index.ts
apps/api/src/index.test.ts
apps/api/src/service-config/rate-limiting/index.ts
apps/api/src/service-config/rate-limiting/index.test.ts
packages/ocom/context-spec/src/index.ts
packages/ocom/context-spec/package.json
packages/ocom/application-services/src/index.ts
packages/ocom/application-services/src/contexts/community/index.ts
packages/ocom/application-services/src/contexts/community/community/index.ts
packages/ocom/application-services/src/contexts/community/community/create.ts
packages/ocom/application-services/package.json
packages/ocom-verification/acceptance-api/src/mock-application-services.ts
packages/ocom-verification/acceptance-api/package.json
Introduce Redis memory-server seedwork and a runnable Redis mock server, and integrate it into local-dev and E2E infrastructure.
  • Add startRedisMemoryServer seedwork that starts an embedded Redis via redis-memory-server and returns connection details plus disposer.
  • Create @cagematch/server-redis-memory-mock app that loads env, configures host/port/version, and starts the memory server with dev and dev:worktree runners.
  • Extend E2E infrastructure to start a Redis test server alongside Mongo/Azurite/Auth and depend on it for the API test server.
  • Add local-dev-config wiring so OCOM API local.settings.json declares REDIS_URL and participates in worktree conversion.
packages/cagematch/server-redis-memory-mock-seedwork/src/index.ts
packages/cagematch/server-redis-memory-mock-seedwork/package.json
packages/cagematch/server-redis-memory-mock-seedwork/README.md
packages/cagematch/server-redis-memory-mock/src/index.ts
packages/cagematch/server-redis-memory-mock/src/setup-environment.ts
packages/cagematch/server-redis-memory-mock/start-dev.ts
packages/cagematch/server-redis-memory-mock/package.json
packages/cagematch/server-redis-memory-mock/README.md
packages/ocom-verification/e2e-tests/src/servers/test-redis-server.ts
packages/ocom-verification/e2e-tests/src/servers/index.ts
packages/ocom-verification/e2e-tests/src/shared/environment/app-paths.ts
packages/ocom-verification/e2e-tests/src/infrastructure.ts
packages/ocom/local-dev-config/src/api-settings/index.ts
packages/ocom/local-dev-config/src/index.test.ts
packages/ocom/local-dev-config/README.md
packages/ocom/local-dev-config/manifest.md
Extend local-dev tooling to support Redis worktree ports and REDIS_URL conversion in line with Mongo and Azurite.
  • Add getRedisPort helper with default 51000 and a non-overlapping 55000+worktreeOffset band for named worktrees.
  • Update WorktreeConversionPlan and convertSettingsForWorktree to support redisKeys and port replacement via getRedisPort.
  • Expose getRedisPort from cellix/local-dev root and worktree index, and document Mongo/Redis/Azurite port behavior in README and manifest.
  • Update local-dev tests to cover Redis port derivation, disjoint port ranges vs Mongo, and REDIS_URL worktree conversion for Azure Functions settings.
  • Extend ocom local-dev-config tests to assert Mongo and Redis URLs are included in worktreeConversion for API local settings.
packages/cellix/local-dev/src/worktree/ports.ts
packages/cellix/local-dev/src/worktree/conversion.ts
packages/cellix/local-dev/src/index.ts
packages/cellix/local-dev/src/worktree/index.ts
packages/cellix/local-dev/src/index.test.ts
packages/ocom/local-dev-config/src/index.test.ts
packages/ocom/local-dev-config/src/api-settings/index.ts
packages/cellix/local-dev/README.md
packages/cellix/local-dev/manifest.md
packages/ocom/local-dev-config/README.md
packages/ocom/local-dev-config/manifest.md
Add Mongo rate-limit model and TTL index, and update workspace configuration/dependencies for new cagematch packages and Redis.
  • Introduce RateLimit mongoose model with TTL index on expiresAt and register it in mongooseContextBuilder and package exports.
  • Add @cagematch/* packages to pnpm-workspace, root scripts, and app/api dependencies; add redis and redis-memory-server to catalog.
  • Update pnpm-lock and Snyk auditConfig overrides, including new ignores and dependency version bumps (brace-expansion, js-yaml, nanoid, fast-uri, etc.).
packages/ocom/data-sources-mongoose-models/src/models/rate-limit/rate-limit.model.ts
packages/ocom/data-sources-mongoose-models/src/index.ts
packages/ocom/data-sources-mongoose-models/package.json
pnpm-workspace.yaml
apps/api/package.json
package.json
pnpm-lock.yaml
.snyk
Document the Redis-backed rate limiting architecture decision and update monorepo docs to reflect new Redis tooling.
  • Add ADR 0034 describing Redis-backed application-service rate limiting, design drivers, Mongo vs Redis tradeoffs, and implementation packages.
  • Update cellix and ocom local-dev documentation to describe Redis port handling, worktree bands, and REDIS_URL conversion.
  • Document cagematch packages and server roles via manifests and READMEs for rate-limiting core, Mongo/Redis contenders, and Redis memory server.
apps/docs/docs/decisions/0034-redis-backed-application-service-rate-limiting.md
packages/cellix/local-dev/README.md
packages/ocom/local-dev-config/README.md
packages/cellix/local-dev/manifest.md
packages/ocom/local-dev-config/manifest.md
packages/cagematch/rate-limiting/manifest.md
packages/cagematch/rate-limiting/README.md
packages/cagematch/rate-limiting-mongo/manifest.md
packages/cagematch/rate-limiting-mongo/README.md
packages/cagematch/rate-limiting-redis/manifest.md
packages/cagematch/rate-limiting-redis/README.md
packages/cagematch/server-redis-memory-mock-seedwork/manifest.md
packages/cagematch/server-redis-memory-mock-seedwork/README.md
packages/cagematch/server-redis-memory-mock/manifest.md
packages/cagematch/server-redis-memory-mock/README.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@jasonmorais
jasonmorais marked this pull request as ready for review August 11, 2026 18:44
@jasonmorais
jasonmorais requested a review from a team August 11, 2026 18:44
@jasonmorais
jasonmorais requested a review from a team as a code owner August 11, 2026 18:44

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 6 issues, and left some high level feedback:

Fixed security issues:

  • brace-expansion (link) · Dashboard

  • fast-uri (link) · Dashboard

  • js-yaml (link)

  • nanoid (link)

  • In apps/api/src/index.ts the isRedis flag is hard-coded to true; consider wiring this to an environment-driven toggle (e.g., CAGEMATCH_USE_REDIS or presence of REDIS_URL) so you can switch between Mongo and Redis backends without code changes.

  • The rate-limit enforcement in Community.create currently throws a generic Error with a string message when limits are exceeded; introducing a specific error type (e.g., RateLimitExceededError carrying feature and timing metadata) would make it easier for callers and middleware to distinguish and handle these failures.

  • ServiceMongoRateLimiting reads COSMOSDB_CONNECTION_STRING and manages its own Mongo client, which duplicates the existing Mongoose/Mongo infrastructure; you may want to align this with the shared connection or make the separation explicit in configuration/comments to avoid future confusion around multiple Mongo clients.

Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `apps/api/src/index.ts` the `isRedis` flag is hard-coded to `true`; consider wiring this to an environment-driven toggle (e.g., `CAGEMATCH_USE_REDIS` or presence of `REDIS_URL`) so you can switch between Mongo and Redis backends without code changes.
- The rate-limit enforcement in `Community.create` currently throws a generic `Error` with a string message when limits are exceeded; introducing a specific error type (e.g., `RateLimitExceededError` carrying feature and timing metadata) would make it easier for callers and middleware to distinguish and handle these failures.
- ServiceMongoRateLimiting reads `COSMOSDB_CONNECTION_STRING` and manages its own Mongo client, which duplicates the existing Mongoose/Mongo infrastructure; you may want to align this with the shared connection or make the separation explicit in configuration/comments to avoid future confusion around multiple Mongo clients.

## Individual Comments

### Comment 1
<location path="apps/docs/docs/decisions/0034-redis-backed-application-service-rate-limiting.md" line_range="19" />
<code_context>
+
+Cellix needs rate limiting for application operations such as `community.create`. The limit must be selected by the feature being executed and may vary by stable caller characteristics such as actor type, role, account, or community scope. It must also work when multiple application instances process requests concurrently. It also requires role/user type based distinctions as well. 
+
+The implementation needs a storage service that can perform a high volume of small counter operations efficiently, expire counters automatically, work during local development, and remain replaceable. The architectural question is which storage sercvice to use and where the rate-limit decision should be made.
+
+## Decision Drivers
</code_context>
<issue_to_address>
**issue (typo):** Fix the typo in "sercvice".

Please correct "sercvice" to "service" in this sentence.

```suggestion
The implementation needs a storage service that can perform a high volume of small counter operations efficiently, expire counters automatically, work during local development, and remain replaceable. The architectural question is which storage service to use and where the rate-limit decision should be made.
```
</issue_to_address>

### Comment 2
<location path="apps/docs/docs/decisions/0034-redis-backed-application-service-rate-limiting.md" line_range="23" />
<code_context>
+
+## Decision Drivers
+
+1. **Fast operations**: Rate-limit checks need to be efficient - theres a lot of possible volume with AI driven requests.
+2. **Feature and role awareness**: Policies must be selected from application context, not only from an HTTP route or client address.
+3. **Distributed consistency**: All instances of the application must observe the same counters. This means a local to machine hosted version is not enough.
</code_context>
<issue_to_address>
**issue (typo):** Use "there's" instead of "theres".

Update this documentation line to use the correct contraction: change "theres" to "there's".

```suggestion
1. **Fast operations**: Rate-limit checks need to be efficient - there's a lot of possible volume with AI driven requests.
```
</issue_to_address>

### Comment 3
<location path="apps/docs/docs/decisions/0034-redis-backed-application-service-rate-limiting.md" line_range="43" />
<code_context>
+
+Chosen option: **Redis-backed rate limiting through a generic application service**, because Redis best meets the requirements for speed, counter operations, automatic expiration, local compatability, and future implementation flexibility.
+
+Redis is the recommended primary service for rate limiting in Cellix. MongoDB remains a compatabile alternative if Redis for whatever reason does not work for the organization, but the speed difference will be very noticable.
+
+The application selects the concrete service when it composes infrastructure services. Service selection is therefore very flexible, and solutions could be swapped out with relative ease.
</code_context>
<issue_to_address>
**issue (typo):** Fix spelling of "compatabile" and "noticable".

Please update the spellings to "compatible" and "noticeable" in this sentence.

Suggested implementation:

```
Chosen option: **Redis-backed rate limiting through a generic application service**, because Redis best meets the requirements for speed, counter operations, automatic expiration, local compatibility, and future implementation flexibility.

```

```
Redis is the recommended primary service for rate limiting in Cellix. MongoDB remains a compatible alternative if Redis for whatever reason does not work for the organization, but the speed difference will be very noticeable.

```
</issue_to_address>

### Comment 4
<location path="apps/docs/docs/decisions/0034-redis-backed-application-service-rate-limiting.md" line_range="49" />
<code_context>
+
+### Service Boundary
+
+Rate limiting is integrated at the **application-service level** rather than at a the register function level for endpoints, or deeper at the domain level.
+
+The application service is the first boundary that has all of the information needed to make the business operation decision without letting the request drive too deep into the application, which also follows our typical infrastructure service usage model:
</code_context>
<issue_to_address>
**issue (typo):** Remove the duplicated article in "at a the register function level".

Change "at a the register function level" to "at the register function level" or "at a register function level" to remove the extra article.

```suggestion
Rate limiting is integrated at the **application-service level** rather than at the register function level for endpoints, or deeper at the domain level.
```
</issue_to_address>

### Comment 5
<location path="apps/docs/docs/decisions/0034-redis-backed-application-service-rate-limiting.md" line_range="59" />
<code_context>
+- the user type or role used by policy criteria
+- the point at which the scenario is about to perform its application work
+
+For example, the application service can pass `feature: 'community.create'` with information about the actual user. The application policy can then give the userusing that feature a limit of five operations per fifteen minutes and staff actors a limit of twenty operations per fifteen minutes.
+
+This placement also means rate limiting applies to an application use case, not just over a single endpoint. The same application service can be called from GraphQL, REST, or some other layer without any difference.
</code_context>
<issue_to_address>
**issue (typo):** Add a space in "userusing".

In this sentence, change "userusing" to "user using".

```suggestion
For example, the application service can pass `feature: 'community.create'` with information about the actual user. The application policy can then give the user using that feature a limit of five operations per fifteen minutes and staff actors a limit of twenty operations per fifteen minutes.
```
</issue_to_address>

### Comment 6
<location path="apps/docs/docs/decisions/0034-redis-backed-application-service-rate-limiting.md" line_range="128" />
<code_context>
+- Bad, because it provides no particular enforcement benefit over the Mongo solution.
+- Bad, because the speed of this solution provides no benefit over the existing Mongo infrastructure.
+
+The time-series option is has been written off a possible solution due to the one benefit it provides not outweighing the negative impact on local development and performance, along with overhead.
+
+## More Information
</code_context>
<issue_to_address>
**suggestion (typo):** Tighten the phrasing in "is has been written off a possible solution".

Rephrase this clause to "has been written off as a possible solution" (remove "is" and add "as") to fix the grammar.

```suggestion
The time-series option has been written off as a possible solution due to the one benefit it provides not outweighing the negative impact on local development and performance, along with overhead.
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread apps/docs/docs/decisions/0034-redis-backed-application-service-rate-limiting.md Outdated
Comment thread apps/docs/docs/decisions/0034-redis-backed-application-service-rate-limiting.md Outdated
Comment thread apps/docs/docs/decisions/0034-redis-backed-application-service-rate-limiting.md Outdated
Comment thread apps/docs/docs/decisions/0034-redis-backed-application-service-rate-limiting.md Outdated
Comment thread apps/docs/docs/decisions/0034-redis-backed-application-service-rate-limiting.md Outdated
Comment thread apps/docs/docs/decisions/0034-redis-backed-application-service-rate-limiting.md Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant