Skip to content

Per-project MaxCheckReads override (#63)#65

Merged
iarunsaragadam merged 3 commits into
mainfrom
feat/per-project-read-budget
Jun 22, 2026
Merged

Per-project MaxCheckReads override (#63)#65
iarunsaragadam merged 3 commits into
mainfrom
feat/per-project-read-budget

Conversation

@iarunsaragadam

Copy link
Copy Markdown
Contributor

Closes #63 — an optional per-project read-budget override so one wide-model tenant can carry a higher (or lower) Check budget without changing the global GATEWAY_MAX_CHECK_READS ceiling for everyone. Mirrors the data_region pattern end-to-end.

  • Proto (additive): Project.max_check_reads, CreateProjectRequest.max_check_reads, UpdateProjectRequest.max_check_reads + clear_max_check_reads. 0 = unset → global default; positive = override; clear = reset to default.
  • Rides the config_json envelope (both drivers, no migration) → resolver resolved.maxCheckReads → threaded per-request into the engine *WithModel calls via effectiveMaxReads(override) (>0 wins, else global). fanOutBudget/expandBudget scale off the effective base identically; BatchCheck inherits per item.
  • Validation: a positive override must be ≥ the shared floor (100); 0/unset allowed. Audit budget_changed.
  • Tests: override trips-below/allows-above the global through the engine AND through the service; per-project isolation; config_json round-trip (both drivers); e2e over Admin+Authz (override completes vs global trips ResourceExhausted; sub-floor → InvalidArgument; clear resets).

Additive proto · go test -race ./... ✓ (real Postgres) · lint 0 ✓.

Closes #63

Add an optional per-project override of the global GATEWAY_MAX_CHECK_READS
read budget, mirroring the data_region per-project envelope. The override
rides projects.config_json (no schema change), is resolved per request, and
threads into the engine's *WithModel entry points as a per-request budget;
0/unset falls back to the fleet default. Validated against the same floor as
the global (config.MinMaxCheckReads).
Add engine, service, conformance, and AdminService+AuthzService e2e tests for
the per-project max_check_reads override: round-trip on GetProject/ListProjects,
the override taking effect (a high override completing a read-heavy Check a
stingy global trips, and a low override tripping where the global allows),
per-project isolation, sub-floor validation, clear-to-default, and config_json
round-trip across both drivers.
Saturate the int32 proto conversion via the existing i32 helper, and document
in README + authorization-model.md that max_check_reads overrides the global
GATEWAY_MAX_CHECK_READS per project (positive replaces the base budget, 0/unset
falls back to the global, clear_max_check_reads resets, same >=100 floor).

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 179d0aa0d8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread pkg/authz/engine.go
return false, err
}
return e.CheckWithModel(ctx, m, projectID, tenantID, namespace, objectID, relation, userID, cc)
return e.CheckWithModel(ctx, m, projectID, tenantID, namespace, objectID, relation, userID, cc, 0)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Apply project budget to product-surface checks

When a project overrides the workspace namespace with a wide rewrite and sets a higher MaxCheckReads, product-surface authorization still ignores that override because Engine.Check always passes 0, so callers using this path fall back to the global budget. In this repo, service.allowed still calls s.engine.Check for workspace role checks (internal/service/service.go:166), so operations like GetWorkspace/AddMember can return ResourceExhausted under a low global budget even though the same project's data-plane Check succeeds with the per-project override.

Useful? React with 👍 / 👎.

@iarunsaragadam iarunsaragadam merged commit 0f1724f into main Jun 22, 2026
10 of 12 checks passed
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.

Per-project/per-tenant MaxCheckReads override

1 participant