Skip to content

Feat/featrue gating and quota enforcement - #15

Merged
arxja merged 14 commits into
mainfrom
feat/featrue-gating-and-quota-enforcement
Jul 22, 2026
Merged

Feat/featrue gating and quota enforcement#15
arxja merged 14 commits into
mainfrom
feat/featrue-gating-and-quota-enforcement

Conversation

@arxja

@arxja arxja commented Jul 21, 2026

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • New Features
    • Added tenant-specific quota enforcement for tracking events, inviting seats, and creating dashboards/reports.
    • Introduced monthly usage tracking for quota decisions.
    • Added tenant-specific API rate limiting with support for unlimited plans.
    • Integrated Arcjet request protection using a token-bucket policy, and enabled Arcjet tooling/Next.js support.
    • Added required Arcjet environment configuration for server startup validation.
  • Tests
    • Added unit tests covering quota enforcement, unlimited handling, and API rate-limit behavior.
  • CI / Configuration
    • Updated CI and test/build environment variables to include Arcjet settings.

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds tenant quota enforcement with monthly usage tracking, Arcjet token-bucket configuration, required Arcjet environment validation, runtime dependencies, CI/test environment values, and unit tests covering quota and rate-limit behavior.

Changes

Tenant quota and rate limiting

Layer / File(s) Summary
Quota models and enforcement
services/quota-tenant.service.ts, lib/database/models/tenant-usage.model.ts
Adds monthly usage persistence and quota checks for events, seats, dashboards, reports, and API rate limits, including unlimited quota handling and counter increments.
Arcjet configuration and runtime setup
lib/arcjet.ts, lib/config/index.ts, package.json, .github/workflows/ci.yml, vitest.config.ts
Adds Arcjet configuration, required environment fields, runtime packages, and Arcjet values for CI and Vitest.
Quota service tests
__tests__/unit/quota-service.test.ts
Tests event quota enforcement, unlimited seats, tenant API limits, and unlimited API rate-limit fallback behavior.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant QuotaService
  participant Tenant
  participant TenantUsage
  QuotaService->>Tenant: Load tenant quotas
  QuotaService->>TenantUsage: Read current monthly usage
  TenantUsage-->>QuotaService: Return event count
  QuotaService-->>QuotaService: Return quota decision
  QuotaService->>TenantUsage: Increment monthly event count
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is related to the main change, covering quota enforcement and gating, even though it contains a typo.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/featrue-gating-and-quota-enforcement

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install timed out. The project may have too many dependencies for the sandbox.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai 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.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lib/arcjet.ts`:
- Around line 15-25: Replace the async per-tenant max callback in the
tokenBucket configuration with a static integer max value, and remove the
associated as any cast. Preserve tenant-specific quota lookup only where
supported, but ensure tokenBucket receives a numeric max directly.

In `@lib/config/index.ts`:
- Around line 22-23: Ensure Vitest/CI supplies valid mock values for the
required ARCJET_KEY and ARCJET_ENV variables before getServerConfig()
initializes, using the existing test setup or environment configuration. Keep
the production schema requirements unchanged and ensure the values satisfy the
Zod constraints.

In `@services/quota-tenant.service.ts`:
- Around line 143-146: Remove the redundant tenantId parameter from
QuotaTenantService.incrementEventCount and update its findOneAndUpdate query and
log.error payload to use this.tenantId. Apply the signature change at
services/quota-tenant.service.ts lines 143-146 and the instance-property
replacements at lines 151-161.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1205f56d-6b7a-4fa6-9642-00b32bb50645

📥 Commits

Reviewing files that changed from the base of the PR and between c5c237b and b9f2864.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (6)
  • __tests__/unit/quota-service.test.ts
  • lib/arcjet.ts
  • lib/config/index.ts
  • lib/database/models/tenant-usage.model.ts
  • package.json
  • services/quota-tenant.service.ts

Comment thread lib/arcjet.ts Outdated
Comment thread lib/config/index.ts
Comment thread services/quota-tenant.service.ts Outdated
@arxja
arxja merged commit 6a67ff9 into main Jul 22, 2026
2 of 3 checks passed

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
services/quota-tenant.service.ts (1)

101-106: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Per-tenant API rate limit is resolved but never enforced. getApiRateLimit() computes a tenant-specific limit, yet nothing actually applies it: checkApiRateLimit always returns allowed: true regardless of the limit, and Arcjet's tokenBucket uses one fixed global capacity for every tenant. Given this PR's goal of tenant quota enforcement, API rate limiting is currently a no-op for all tenants.

  • services/quota-tenant.service.ts#L101-L106: Make checkApiRateLimit actually compare a request/usage count against limit and return allowed: false when exceeded, instead of always returning allowed: true.
  • lib/arcjet.ts#L4-L16: Either accept that Arcjet enforces only a shared floor/ceiling and rely on checkApiRateLimit (once fixed) for per-tenant enforcement, or use Arcjet's supported mechanism (e.g., a characteristics-scoped rule per tenant) to vary capacity — the current static capacity: 100 combined with the leftover TODO comment indicates this was never completed.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@services/quota-tenant.service.ts` around lines 101 - 106, The per-tenant API
limit is resolved but never enforced. In services/quota-tenant.service.ts lines
101-106, update checkApiRateLimit to compare the current request or usage count
against limit and return allowed: false once exceeded, preserving the limit in
the result. In lib/arcjet.ts lines 4-16, either explicitly retain Arcjet as a
shared floor/ceiling while relying on checkApiRateLimit for tenant enforcement
and remove the incomplete TODO, or configure a supported tenant-scoped
characteristics rule so capacity varies by tenant; do not leave the static
global capacity as the sole enforcement.
🧹 Nitpick comments (1)
.github/workflows/ci.yml (1)

91-92: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Minor: quote ARCJET_KEY for consistency.

Every other value in this env block is quoted; ajkey_dummy on Line 92 is bare while the test job's equivalent (Line 47) is quoted.

Proposed fix
-          ARCJET_KEY: ajkey_dummy
+          ARCJET_KEY: "ajkey_dummy"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml around lines 91 - 92, Quote the ARCJET_KEY value in
the environment block for consistency with the surrounding variables and the
equivalent test-job configuration, without changing its value.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@services/quota-tenant.service.ts`:
- Around line 148-158: Update the increment flow around
TenantUsage.findOneAndUpdate so database failures are surfaced to callers
instead of being swallowed after logging. Rethrow the caught error, or propagate
an equivalent failure indicator, and ensure callers such as canTrackEvent can
fail closed or otherwise handle an unsuccessful counter update.

---

Outside diff comments:
In `@services/quota-tenant.service.ts`:
- Around line 101-106: The per-tenant API limit is resolved but never enforced.
In services/quota-tenant.service.ts lines 101-106, update checkApiRateLimit to
compare the current request or usage count against limit and return allowed:
false once exceeded, preserving the limit in the result. In lib/arcjet.ts lines
4-16, either explicitly retain Arcjet as a shared floor/ceiling while relying on
checkApiRateLimit for tenant enforcement and remove the incomplete TODO, or
configure a supported tenant-scoped characteristics rule so capacity varies by
tenant; do not leave the static global capacity as the sole enforcement.

---

Nitpick comments:
In @.github/workflows/ci.yml:
- Around line 91-92: Quote the ARCJET_KEY value in the environment block for
consistency with the surrounding variables and the equivalent test-job
configuration, without changing its value.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 13788ca6-77a1-4973-88ad-005ce6a22666

📥 Commits

Reviewing files that changed from the base of the PR and between b9f2864 and 7558981.

📒 Files selected for processing (4)
  • .github/workflows/ci.yml
  • lib/arcjet.ts
  • services/quota-tenant.service.ts
  • vitest.config.ts

Comment on lines +148 to +158
try {
await TenantUsage.findOneAndUpdate(
{ tenantId: this.tenantId, year, month },
{ $inc: { count: amount } },
{ upsert: true, new: true },
);
} catch (error) {
log.error("Failed to increment event count", error as Error, {
tenantId: this.tenantId,
});
}

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.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Swallowed increment errors can silently bypass quota enforcement.

If the findOneAndUpdate upsert fails, the error is only logged — the caller (and thus canTrackEvent's subsequent reads) has no way to know the usage counter wasn't updated. Since this counter is the sole source of truth for canTrackEvent, transient DB failures let a tenant's real usage silently drift below what's tracked, undermining the quota it enforces.

Consider surfacing the failure (rethrow, or return a success/failure indicator) so callers can decide whether to fail closed for quota-sensitive paths.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@services/quota-tenant.service.ts` around lines 148 - 158, Update the
increment flow around TenantUsage.findOneAndUpdate so database failures are
surfaced to callers instead of being swallowed after logging. Rethrow the caught
error, or propagate an equivalent failure indicator, and ensure callers such as
canTrackEvent can fail closed or otherwise handle an unsuccessful counter
update.

@arxja
arxja deleted the feat/featrue-gating-and-quota-enforcement branch July 22, 2026 12:29
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