Skip to content

Unify API access tiers into a single UserApiAccess model - #5198

Draft
lsabor wants to merge 1 commit into
mainfrom
unify-api-access-tier
Draft

lsabor wants to merge 1 commit into
mainfrom
unify-api-access-tier

Conversation

@lsabor

@lsabor lsabor commented Sep 21, 2026

Copy link
Copy Markdown
Contributor

The API access tier a user held was split across two fields that behaved differently and neither documented the difference: User.api_access_tier set the gateway's ruleset, while UserDataAccess.api_access_tier was read only on project-scoped rows and was inert everywhere else. A project-scoped row and a base tier granted overlapping but unequal access, and the model's help text described an "exceeds the user's base tier" comparison that no code implemented.

UserApiAccess replaces both. A user with no row is restricted; a row with no project is global; a row with a project applies to that project; the most permissive applicable level wins, so a project grant can only widen a global one. There is no stored restricted level, because a grant conferring it would be indistinguishable from having no grant.

UserDataAccess goes back to being purely a read whitelist (view_user_data / view_deanonymized_data), unrelated to API tiers.

The serializer keeps emitting api_access_tier and project_data_access unchanged, so this works against the currently deployed gateway with no coordinated release.

The data migration maps a global benchmarking tier to a grant scoped to the benchmarking project rather than a global grant, because that tier only ever unlocked that one project; unrestricted stays global. Inert tiers (on post-scoped or unscoped whitelist rows) are deliberately not carried over.

Verified live against the gateway: field visibility under a project-scoped benchmarking grant is identical to the old global benchmarking tier, and a project grant correctly does not leak to other projects.

Claude-Session: https://claude.ai/code/session_01KpPsEyn1D9Yc6GhS7NA8pu

The API access tier a user held was split across two fields that behaved
differently and neither documented the difference: User.api_access_tier set the
gateway's ruleset, while UserDataAccess.api_access_tier was read only on
project-scoped rows and was inert everywhere else. A project-scoped row and a
base tier granted overlapping but unequal access, and the model's help text
described an "exceeds the user's base tier" comparison that no code implemented.

UserApiAccess replaces both. A user with no row is restricted; a row with no
project is global; a row with a project applies to that project; the most
permissive applicable level wins, so a project grant can only widen a global
one. There is no stored `restricted` level, because a grant conferring it would
be indistinguishable from having no grant.

UserDataAccess goes back to being purely a read whitelist (view_user_data /
view_deanonymized_data), unrelated to API tiers.

The serializer keeps emitting `api_access_tier` and `project_data_access`
unchanged, so this works against the currently deployed gateway with no
coordinated release.

The data migration maps a global `benchmarking` tier to a grant scoped to the
benchmarking project rather than a global grant, because that tier only ever
unlocked that one project; `unrestricted` stays global. Inert tiers (on
post-scoped or unscoped whitelist rows) are deliberately not carried over.

Verified live against the gateway: field visibility under a project-scoped
benchmarking grant is identical to the old global benchmarking tier, and a
project grant correctly does not leak to other projects.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KpPsEyn1D9Yc6GhS7NA8pu
@lsabor
lsabor deployed to testing_env September 21, 2026 19:23 — with GitHub Actions Active
@lsabor
lsabor deployed to testing_env September 21, 2026 19:23 — with GitHub Actions Active
@coderabbitai

coderabbitai Bot commented Sep 21, 2026

Copy link
Copy Markdown
Contributor

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

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.

@github-actions

Copy link
Copy Markdown
Contributor

🚀 Preview Environment

Your preview environment is ready!

Resource Details
🌐 Preview URL https://metaculus-pr-5198-unify-api-access-tier-preview.mtcl.cc
📦 Docker Image ghcr.io/metaculus/metaculus:unify-api-access-tier-4f78bdc
🗄️ PostgreSQL NeonDB branch preview/pr-5198-unify-api-access-tier
Redis Fly Redis mtc-redis-pr-5198-unify-api-access-tier

Details

  • Commit: cd222b7e745068595b49852913f6902247e531a2
  • Branch: unify-api-access-tier
  • Fly App: metaculus-pr-5198-unify-api-access-tier

ℹ️ Preview Environment Info

Isolation:

  • PostgreSQL and Redis are fully isolated from production
  • Each PR gets its own database branch and Redis instance
  • Changes pushed to this PR will trigger a new deployment

Limitations:

  • Background workers and cron jobs are not deployed in preview environments
  • If you need to test background jobs, use Heroku staging environments

Cleanup:

  • This preview will be automatically destroyed when the PR is closed

This branch was successfully deployed

2 active deployments
Preview 4f78bdc0 Deployed Sep 21, 2026 by lsabor via Deploy Preview App #4183
testing_env 4f78bdc0 Deployed Sep 21, 2026 by lsabor via Backend Checks #8382
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