feat(plans): flag-gated per-service resource-count caps (Task #55)#263
Merged
Conversation
Closes the strict-≥80%-margin hole where only queue_count was capped: a tenant
could create MANY postgres/vector/redis/mongodb/storage resources each at the
per-resource size cap and blow the saturated-COGS bound (Redis binding at
$6.50/GB). Adds a per-tier active-resource COUNT cap per service, enforced like
the existing queue_count A6 block — but FLAG-GATED, default OFF.
Flag: RESOURCE_COUNT_CAPS_ENABLED (config.go, default false). When off, the new
enforceResourceCountCap helper returns immediately and runs NO count query —
zero behavior change, proven inert by TestResourceCountCap_FlagOffIsInert +
TestEnforceResourceCountCap_FlagOffInert_Whitebox. Operator enables after a usage
audit so no current tenant is retroactively over a cap.
Enforcement sites (mirror queue.go's A6 block; 402 + agent_action + metric):
db.go (postgres), vector.go, cache.go (redis), nosql.go (mongodb), storage.go.
Shared helper: internal/handlers/resource_count_cap.go (one call site per
handler, not a copy-pasted block). Count cap fails CLOSED on a count-query error
when enabled (a cheap indexed COUNT; must not silently bypass a cost cap).
Per-tier numbers (api/plans.yaml; mirrors common defaultYAML, depends on
common#47 which is merged): anon/free=1 each; hobby=2; hobby_plus=3;
pro pg/vec/mongo/storage=5 redis=3; growth=6 redis=3; team pg=5 vec=8 redis=4
mongo=6 storage=6. redis_count is the most conservative line everywhere (binding
COGS). Derived so count×size×unit-COGS ≤ tier 20%-of-price budget per service.
Surfaces (rule 22): /api/v1/capabilities resource_count_limit map;
/api/v1/billing/usage count+count_limit on storage services; openapi.go schemas;
content/llms.txt + instanode-web public/llms.txt (separate PRs).
Metric (rule 25): instant_resource_count_limit_blocked_total{service,team_tier}
(metrics.go). Alert + Prom rule + dashboard tile + catalog row in infra PR.
Tests: registry-iterating flag-on guard (rule 18,
TestResourceCountCap_FlagOnAtLimitRejects) so service N+1 can't ship uncapped;
under-limit pass; whitebox edge branches (unlimited, count-error, nil cfg) →
enforceResourceCountCap 100%; capabilities surface guard; config flag test;
strict_margin guard extended to the new *_count fields. make gate green (the one
unrelated pre-existing failure, models.TestLinkGitHubID, reproduces with this
change stashed and touches no files in this diff).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…limit (Task #55) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
mastermanas805
added a commit
that referenced
this pull request
Jun 5, 2026
# Conflicts: # internal/config/config.go # internal/config/config_test.go
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Closes the biggest remaining strict-≥80%-margin hole: today only
queue_countis capped, so a tenant can create MANY postgres/vector/redis/mongodb/storage resources each at the per-resource size cap and blow the saturated-COGS bound (Redis the binding constraint at $6.50/GB-mo). This adds a per-tier active-resource COUNT cap per service, enforced like the existingqueue_countA6 block — but flag-gated, default OFF, so it cannot surprise-break existing heavy tenants with a 402.Depends on common#47 (merged) for the
ResourceCountLimitaccessors.Flag — default OFF, inert when off
RESOURCE_COUNT_CAPS_ENABLED(config.go, default false). When off,enforceResourceCountCapreturns immediately and runs no count query — zero behavior change. Proven inert byTestResourceCountCap_FlagOffIsInert(HTTP) +TestEnforceResourceCountCap_FlagOffInert_Whitebox. Operator enables after a usage audit.Enforcement sites (mirror queue.go A6: 402 + agent_action + metric)
db.go(postgres),vector.go,cache.go(redis),nosql.go(mongodb),storage.gointernal/handlers/resource_count_cap.go— one call site per handler, not a copy-pasted block. Fails closed on a count-query error when enabled (cheap indexed COUNT; must not silently bypass a cost cap).Per-tier counts (COGS-derived; redis the binding line)
Invariant:
count × per-resource-size-cap × unit-COGS ≤ tier 20%-of-price budgetper service.api/plans.yamlmirrorscommondefaultYAML.Surfaces (rule 22)
/api/v1/capabilities→resource_count_limitmap (registry-iterating, auto-surfaces new tiers/services)./api/v1/billing/usage→count+count_limiton storage services.openapi.goschemas for both.content/llms.txt+instanode-webpublic/llms.txt — separate PRs.Metric (rule 25)
instant_resource_count_limit_blocked_total{service,team_tier}. Alert + Prom rule + dashboard tile + catalog row in infra PR (resource-count-caps-observability).Tests
TestResourceCountCap_FlagOnAtLimitRejects— rule-18 registry-iterating guard: every count-capped service 402s at cap when on (service N+1 can't ship uncapped).enforceResourceCountCap100%.strict_marginguard extended to the new*_countfields.make gategreen. The one unrelated failure (models.TestLinkGitHubID) is pre-existing — reproduces with this change stashed and touches no file in this diff.Ship note
Flag is OFF in prod → enforcement inert until the operator runs
kubectl set env -n instant deploy/instant-api RESOURCE_COUNT_CAPS_ENABLED=trueafter a usage audit. Awaiting operator enable of RESOURCE_COUNT_CAPS_ENABLED.🤖 Generated with Claude Code