Skip to content

feat: SQL like queries for permissions#3617

Merged
chronark merged 8 commits intomainfrom
07-16-feat_sql_like_queries_for_permissions
Jul 17, 2025
Merged

feat: SQL like queries for permissions#3617
chronark merged 8 commits intomainfrom
07-16-feat_sql_like_queries_for_permissions

Conversation

@chronark
Copy link
Collaborator

@chronark chronark commented Jul 16, 2025

This POC replaces the complex oneOf union type in the verifyKey endpoint's
permissions field with a simple string-based query system. The previous union type was
a nightmare for SDK generation, creating confusing type definitions and poor
developer experience in generated client libraries. Now we have a clean string field
that generates beautiful, type-safe SDKs across all languages.

The new system supports SQL-like permission queries with AND/OR operators and
parentheses, while maintaining a simple API surface. This change dramatically
improves both our code generation quality and end-user experience - developers can now
write intuitive queries like "users.read AND (posts.write OR posts.delete)" instead
of constructing complex nested objects.

  // Before: Complex union type (SDK nightmare)
  {
    permissions: {
      type: "and",
      permissions: ["users.read", "users.write"]
    }
  }
  // After: Clean string query (SDK friendly)
  {
    permissions: "users.read AND users.write"
  }

What does this PR do?

This PR enhances the permissions query system in the verifyKey endpoint by replacing the previous object-based permission structure with a more flexible SQL-like query string syntax. The new implementation includes:

  1. A new parser for SQL-like permission queries that supports logical operators (AND, OR) and parentheses for grouping
  2. Comprehensive error handling with detailed error messages for syntax errors
  3. Documentation for the new permissions query syntax with examples and error cases
  4. Updated OpenAPI schema to reflect the new string-based query format

Fixes #

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • Chore (refactoring code, technical debt, workflow improvements)
  • Enhancement (small improvements)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How should this be tested?

  • Test simple permission queries like "api.read"
  • Test complex queries with AND/OR operators: "api.read AND api.write"
  • Test queries with parentheses: "(api.read OR api.write) AND users.view"
  • Test error cases like unmatched parentheses, invalid characters, and missing operands
  • Verify that error messages are clear and helpful
  • Test with large permission sets (20+ permissions) to ensure performance

Checklist

Required

  • Filled out the "How to test" section in this PR
  • Read Contributing Guide
  • Self-reviewed my own code
  • Commented on my code in hard-to-understand areas
  • Ran pnpm build
  • Ran pnpm fmt
  • Checked for warnings, there are none
  • Removed all console.logs
  • Merged the latest changes from main onto my branch with git pull origin main
  • My changes don't cause any responsiveness issues

Appreciated

  • If a UI change was made: Added a screen recording or screenshots to this PR
  • Updated the Unkey Docs if changes were necessary

Summary by CodeRabbit

  • New Features

    • Permissions for the verifyKey API endpoint can now be specified using a SQL-like query string, supporting logical operators (AND, OR) and parentheses for grouping.
    • Enhanced error handling for invalid permissions query syntax, with detailed error messages and documentation.
    • Added comprehensive documentation and examples for permissions query syntax and error responses.
  • Bug Fixes

    • Improved error reporting for permissions query syntax errors, returning clear 400 Bad Request responses.
  • Documentation

    • Expanded API and RBAC documentation to cover the new permissions query format, valid syntax, and common errors.
    • Added new API reference entries for permissions query syntax errors.
  • Tests

    • Added and updated tests to cover complex permissions queries, large queries, and invalid syntax scenarios.
  • Refactor

    • Simplified permissions handling in the API by replacing structured permissions objects with a single query string.

@vercel
Copy link

vercel bot commented Jul 16, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
dashboard ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jul 17, 2025 2:30pm
engineering ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jul 17, 2025 2:30pm

@changeset-bot
Copy link

changeset-bot bot commented Jul 16, 2025

⚠️ No Changeset found

Latest commit: ce0e86d

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jul 16, 2025

📝 Walkthrough

Walkthrough

A new SQL-like query syntax for permissions was introduced for the verifyKey API, replacing the previous structured object format. This includes a new lexer and parser in the RBAC package, updated OpenAPI schema, enhanced error handling with a dedicated error code, expanded documentation, and comprehensive tests for parsing, lexing, and integration.

Changes

Files / Areas Change Summary
go/pkg/rbac/lexer.go, go/pkg/rbac/parser.go, go/pkg/rbac/lexer_test.go, go/pkg/rbac/parse_test.go, go/pkg/rbac/integration_test.go Added a lexer and recursive descent parser for SQL-like permission queries with comprehensive unit and integration tests.
go/pkg/rbac/rbac.go, go/pkg/rbac/doc.go Added public ParseQuery function, improved error handling, and expanded documentation for query syntax and usage.
go/apps/api/openapi/openapi.yaml, go/apps/api/openapi/gen.go Changed the permissions field from a union/object to a single SQL-like query string in OpenAPI schema and generated Go types.
go/apps/api/routes/v2_keys_verify_key/handler.go Replaced legacy permissions conversion with direct parsing using the new RBAC parser and enhanced error wrapping.
go/apps/api/routes/v2_keys_verify_key/200_test.go, go/apps/api/routes/v2_keys_verify_key/400_test.go Updated and expanded tests to use the new permissions query syntax, including complex and invalid query scenarios.
go/pkg/codes/constants_gen.go, go/pkg/codes/user_request.go, go/pkg/codes/generate.go Added new error code and structured error grouping for permissions query syntax errors under the user error domain.
go/pkg/zen/middleware_errors.go Updated error middleware to return HTTP 400 for the new permissions query syntax error code.
apps/docs/api-reference/errors-v2/user/bad_request/permissions_query_syntax_error.mdx, apps/docs/docs.json Added documentation for the new error and updated the API reference structure.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant API
    participant RBAC
    participant ErrorHandler

    Client->>API: POST /v2/keys.verifyKey (permissions query string)
    API->>RBAC: ParseQuery(permissions string)
    alt Valid query
        RBAC-->>API: PermissionQuery AST
        API->>RBAC: EvaluatePermissions(PermissionQuery, user permissions)
        RBAC-->>API: Evaluation result
        API-->>Client: Success/Failure response
    else Invalid query syntax
        RBAC-->>API: Parse error
        API->>ErrorHandler: Wrap error with codes.UserErrorsBadRequestPermissionsQuerySyntaxError
        ErrorHandler-->>Client: 400 Bad Request with error details
    end
Loading

Suggested labels

:joystick: 750 points, 🕹️ oss.gg, hacktoberfest

Suggested reviewers

  • mcstepp
  • MichaelUnkey
  • perkinsjr
  • ogzhanolguncu
✨ Finishing Touches
  • 📝 Generate Docstrings

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Collaborator Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

@github-actions
Copy link
Contributor

github-actions bot commented Jul 16, 2025

Thank you for following the naming conventions for pull request titles! 🙏

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between be3f201 and 53d4c67.

📒 Files selected for processing (19)
  • apps/docs/api-reference/errors-v2/user/bad_request/permissions_query_syntax_error.mdx (1 hunks)
  • apps/docs/docs.json (1 hunks)
  • go/apps/api/openapi/gen.go (1 hunks)
  • go/apps/api/openapi/openapi.yaml (2 hunks)
  • go/apps/api/routes/v2_keys_verify_key/200_test.go (3 hunks)
  • go/apps/api/routes/v2_keys_verify_key/400_test.go (1 hunks)
  • go/apps/api/routes/v2_keys_verify_key/handler.go (1 hunks)
  • go/pkg/codes/constants_gen.go (2 hunks)
  • go/pkg/codes/generate.go (1 hunks)
  • go/pkg/codes/unkey_application.go (3 hunks)
  • go/pkg/codes/user_request.go (1 hunks)
  • go/pkg/rbac/doc.go (3 hunks)
  • go/pkg/rbac/integration_test.go (1 hunks)
  • go/pkg/rbac/lexer.go (1 hunks)
  • go/pkg/rbac/lexer_test.go (1 hunks)
  • go/pkg/rbac/parse_test.go (1 hunks)
  • go/pkg/rbac/parser.go (1 hunks)
  • go/pkg/rbac/rbac.go (3 hunks)
  • go/pkg/zen/middleware_errors.go (1 hunks)
🧰 Additional context used
🧠 Learnings (11)
📓 Common learnings
Learnt from: chronark
PR: unkeyed/unkey#3560
File: go/apps/api/routes/v2_keys_create_key/handler.go:468-581
Timestamp: 2025-07-15T14:47:20.490Z
Learning: In the Unkey codebase, role and permission names are validated at the OpenAPI schema layer with strict regex patterns: role names must match "^[a-zA-Z][a-zA-Z0-9_-]*$" (start with letter, followed by letters/numbers/underscores/hyphens) and permission names must match "^[a-zA-Z0-9_]+$" (letters, numbers, underscores only). This validation occurs during zen.BindBody call before handlers run, preventing malicious or improperly formatted names from reaching auto-creation logic.
Learnt from: chronark
PR: unkeyed/unkey#3560
File: go/apps/api/routes/v2_keys_create_key/handler.go:353-466
Timestamp: 2025-07-15T14:25:05.608Z
Learning: In the Unkey codebase, input validation for API endpoints is handled at the OpenAPI schema layer, which validates request fields like permission slugs (pattern: "^[a-zA-Z0-9_]+$", length: 1-100 characters) before requests reach the handler code. This validation occurs during the zen.BindBody call in handlers.
Learnt from: Flo4604
PR: unkeyed/unkey#3421
File: go/apps/api/openapi/openapi.yaml:196-200
Timestamp: 2025-07-03T05:58:10.699Z
Learning: In the Unkey codebase, OpenAPI 3.1 is used, which allows sibling keys (such as `description`) alongside `$ref` in schema objects. Do not flag this as an error in future reviews.
go/pkg/zen/middleware_errors.go (3)
Learnt from: chronark
PR: unkeyed/unkey#3560
File: go/apps/api/routes/v2_keys_create_key/handler.go:353-466
Timestamp: 2025-07-15T14:25:05.608Z
Learning: In the Unkey codebase, input validation for API endpoints is handled at the OpenAPI schema layer, which validates request fields like permission slugs (pattern: "^[a-zA-Z0-9_]+$", length: 1-100 characters) before requests reach the handler code. This validation occurs during the zen.BindBody call in handlers.
Learnt from: chronark
PR: unkeyed/unkey#3560
File: go/apps/api/routes/v2_keys_create_key/handler.go:468-581
Timestamp: 2025-07-15T14:47:20.490Z
Learning: In the Unkey codebase, role and permission names are validated at the OpenAPI schema layer with strict regex patterns: role names must match "^[a-zA-Z][a-zA-Z0-9_-]*$" (start with letter, followed by letters/numbers/underscores/hyphens) and permission names must match "^[a-zA-Z0-9_]+$" (letters, numbers, underscores only). This validation occurs during zen.BindBody call before handlers run, preventing malicious or improperly formatted names from reaching auto-creation logic.
Learnt from: Flo4604
PR: unkeyed/unkey#2955
File: go/apps/api/routes/v2_identities_create_identity/handler.go:162-202
Timestamp: 2025-03-19T09:25:59.751Z
Learning: In the Unkey codebase, input validation for API endpoints is primarily handled through OpenAPI schema validation, which occurs before requests reach the handler code. For example, in the identities.createIdentity endpoint, minimum values for ratelimit duration and limit are defined in the OpenAPI schema rather than duplicating these checks in the handler.
go/apps/api/routes/v2_keys_verify_key/400_test.go (3)
Learnt from: MichaelUnkey
PR: unkeyed/unkey#2114
File: apps/api/src/routes/v1_keys_updateKey.error.test.ts:0-0
Timestamp: 2024-09-27T15:20:05.475Z
Learning: In the `v1/keys.updateKey` endpoint, the server validates the refill configuration before checking if the key exists. Therefore, tests can assert validation errors without needing to create the key first.
Learnt from: chronark
PR: unkeyed/unkey#3560
File: go/apps/api/routes/v2_keys_create_key/handler.go:353-466
Timestamp: 2025-07-15T14:25:05.608Z
Learning: In the Unkey codebase, input validation for API endpoints is handled at the OpenAPI schema layer, which validates request fields like permission slugs (pattern: "^[a-zA-Z0-9_]+$", length: 1-100 characters) before requests reach the handler code. This validation occurs during the zen.BindBody call in handlers.
Learnt from: chronark
PR: unkeyed/unkey#3560
File: go/apps/api/routes/v2_keys_create_key/handler.go:468-581
Timestamp: 2025-07-15T14:47:20.490Z
Learning: In the Unkey codebase, role and permission names are validated at the OpenAPI schema layer with strict regex patterns: role names must match "^[a-zA-Z][a-zA-Z0-9_-]*$" (start with letter, followed by letters/numbers/underscores/hyphens) and permission names must match "^[a-zA-Z0-9_]+$" (letters, numbers, underscores only). This validation occurs during zen.BindBody call before handlers run, preventing malicious or improperly formatted names from reaching auto-creation logic.
go/pkg/rbac/rbac.go (1)
Learnt from: Flo4604
PR: unkeyed/unkey#3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.464Z
Learning: For debugging database replica usage in go/pkg/db/replica.go, it's acceptable to mark QueryRowContext operations as "success" even though SQL errors only surface during row.Scan() calls. The timing metrics are the primary concern for debugging replica performance patterns.
go/pkg/rbac/doc.go (1)
Learnt from: chronark
PR: unkeyed/unkey#3420
File: go/pkg/hydra/store/gorm/gorm.go:486-498
Timestamp: 2025-07-02T11:51:58.572Z
Learning: The Hydra package (go/pkg/hydra) is planned to be migrated from GORM to sqlc for database operations, which explains why raw SQL queries are acceptable in the current implementation.
go/apps/api/routes/v2_keys_verify_key/handler.go (2)
Learnt from: chronark
PR: unkeyed/unkey#3560
File: go/apps/api/routes/v2_keys_create_key/handler.go:468-581
Timestamp: 2025-07-15T14:47:20.490Z
Learning: In the Unkey codebase, role and permission names are validated at the OpenAPI schema layer with strict regex patterns: role names must match "^[a-zA-Z][a-zA-Z0-9_-]*$" (start with letter, followed by letters/numbers/underscores/hyphens) and permission names must match "^[a-zA-Z0-9_]+$" (letters, numbers, underscores only). This validation occurs during zen.BindBody call before handlers run, preventing malicious or improperly formatted names from reaching auto-creation logic.
Learnt from: chronark
PR: unkeyed/unkey#3560
File: go/apps/api/routes/v2_keys_create_key/handler.go:353-466
Timestamp: 2025-07-15T14:25:05.608Z
Learning: In the Unkey codebase, input validation for API endpoints is handled at the OpenAPI schema layer, which validates request fields like permission slugs (pattern: "^[a-zA-Z0-9_]+$", length: 1-100 characters) before requests reach the handler code. This validation occurs during the zen.BindBody call in handlers.
go/pkg/codes/constants_gen.go (1)
Learnt from: ogzhanolguncu
PR: unkeyed/unkey#3321
File: apps/dashboard/lib/trpc/routers/authorization/roles/keys/schema-with-helpers.ts:5-8
Timestamp: 2025-06-18T12:28:10.449Z
Learning: In the unkey dashboard application, API validation for pagination limits is controlled at the UI level rather than requiring additional server-side validation, as the APIs are internal and protected by UI logic.
go/apps/api/routes/v2_keys_verify_key/200_test.go (4)
Learnt from: MichaelUnkey
PR: unkeyed/unkey#2114
File: apps/api/src/routes/v1_keys_updateKey.error.test.ts:0-0
Timestamp: 2024-09-27T15:20:05.475Z
Learning: In the `v1/keys.updateKey` endpoint, the server validates the refill configuration before checking if the key exists. Therefore, tests can assert validation errors without needing to create the key first.
Learnt from: chronark
PR: unkeyed/unkey#3560
File: go/apps/api/routes/v2_keys_create_key/handler.go:353-466
Timestamp: 2025-07-15T14:25:05.608Z
Learning: In the Unkey codebase, input validation for API endpoints is handled at the OpenAPI schema layer, which validates request fields like permission slugs (pattern: "^[a-zA-Z0-9_]+$", length: 1-100 characters) before requests reach the handler code. This validation occurs during the zen.BindBody call in handlers.
Learnt from: chronark
PR: unkeyed/unkey#3560
File: go/apps/api/routes/v2_keys_create_key/handler.go:468-581
Timestamp: 2025-07-15T14:47:20.490Z
Learning: In the Unkey codebase, role and permission names are validated at the OpenAPI schema layer with strict regex patterns: role names must match "^[a-zA-Z][a-zA-Z0-9_-]*$" (start with letter, followed by letters/numbers/underscores/hyphens) and permission names must match "^[a-zA-Z0-9_]+$" (letters, numbers, underscores only). This validation occurs during zen.BindBody call before handlers run, preventing malicious or improperly formatted names from reaching auto-creation logic.
Learnt from: Flo4604
PR: unkeyed/unkey#2955
File: go/apps/api/routes/v2_identities_create_identity/handler.go:162-202
Timestamp: 2025-03-19T09:25:59.751Z
Learning: In the Unkey codebase, input validation for API endpoints is primarily handled through OpenAPI schema validation, which occurs before requests reach the handler code. For example, in the identities.createIdentity endpoint, minimum values for ratelimit duration and limit are defined in the OpenAPI schema rather than duplicating these checks in the handler.
apps/docs/api-reference/errors-v2/user/bad_request/permissions_query_syntax_error.mdx (4)
Learnt from: chronark
PR: unkeyed/unkey#3560
File: go/apps/api/routes/v2_keys_create_key/handler.go:353-466
Timestamp: 2025-07-15T14:25:05.608Z
Learning: In the Unkey codebase, input validation for API endpoints is handled at the OpenAPI schema layer, which validates request fields like permission slugs (pattern: "^[a-zA-Z0-9_]+$", length: 1-100 characters) before requests reach the handler code. This validation occurs during the zen.BindBody call in handlers.
Learnt from: chronark
PR: unkeyed/unkey#3560
File: go/apps/api/routes/v2_keys_create_key/handler.go:468-581
Timestamp: 2025-07-15T14:47:20.490Z
Learning: In the Unkey codebase, role and permission names are validated at the OpenAPI schema layer with strict regex patterns: role names must match "^[a-zA-Z][a-zA-Z0-9_-]*$" (start with letter, followed by letters/numbers/underscores/hyphens) and permission names must match "^[a-zA-Z0-9_]+$" (letters, numbers, underscores only). This validation occurs during zen.BindBody call before handlers run, preventing malicious or improperly formatted names from reaching auto-creation logic.
Learnt from: ogzhanolguncu
PR: unkeyed/unkey#3375
File: apps/dashboard/app/(app)/settings/root-keys/components/table/hooks/use-root-keys-list-query.ts:0-0
Timestamp: 2025-06-25T20:32:10.471Z
Learning: In the Unkey codebase, ogzhanolguncu prefers strict validation with fail-fast error handling. When validation errors occur that shouldn't happen in normal operation (like invalid operators), throwing errors to crash the page is preferred over graceful error handling or console logging.
Learnt from: Flo4604
PR: unkeyed/unkey#2955
File: go/apps/api/routes/v2_identities_create_identity/handler.go:162-202
Timestamp: 2025-03-19T09:25:59.751Z
Learning: In the Unkey codebase, input validation for API endpoints is primarily handled through OpenAPI schema validation, which occurs before requests reach the handler code. For example, in the identities.createIdentity endpoint, minimum values for ratelimit duration and limit are defined in the OpenAPI schema rather than duplicating these checks in the handler.
go/apps/api/openapi/openapi.yaml (5)

undefined

<retrieved_learning>
Learnt from: chronark
PR: #3560
File: go/apps/api/routes/v2_keys_create_key/handler.go:353-466
Timestamp: 2025-07-15T14:25:05.608Z
Learning: In the Unkey codebase, input validation for API endpoints is handled at the OpenAPI schema layer, which validates request fields like permission slugs (pattern: "^[a-zA-Z0-9_]+$", length: 1-100 characters) before requests reach the handler code. This validation occurs during the zen.BindBody call in handlers.
</retrieved_learning>

<retrieved_learning>
Learnt from: chronark
PR: #3560
File: go/apps/api/routes/v2_keys_create_key/handler.go:468-581
Timestamp: 2025-07-15T14:47:20.490Z
Learning: In the Unkey codebase, role and permission names are validated at the OpenAPI schema layer with strict regex patterns: role names must match "^[a-zA-Z][a-zA-Z0-9_-]*$" (start with letter, followed by letters/numbers/underscores/hyphens) and permission names must match "^[a-zA-Z0-9_]+$" (letters, numbers, underscores only). This validation occurs during zen.BindBody call before handlers run, preventing malicious or improperly formatted names from reaching auto-creation logic.
</retrieved_learning>

<retrieved_learning>
Learnt from: Flo4604
PR: #3421
File: go/apps/api/openapi/openapi.yaml:196-200
Timestamp: 2025-07-03T05:58:10.699Z
Learning: In the Unkey codebase, OpenAPI 3.1 is used, which allows sibling keys (such as description) alongside $ref in schema objects. Do not flag this as an error in future reviews.
</retrieved_learning>

<retrieved_learning>
Learnt from: Flo4604
PR: #2955
File: go/apps/api/routes/v2_identities_create_identity/handler.go:162-202
Timestamp: 2025-03-19T09:25:59.751Z
Learning: In the Unkey codebase, input validation for API endpoints is primarily handled through OpenAPI schema validation, which occurs before requests reach the handler code. For example, in the identities.createIdentity endpoint, minimum values for ratelimit duration and limit are defined in the OpenAPI schema rather than duplicating these checks in the handler.
</retrieved_learning>

<retrieved_learning>
Learnt from: Flo4604
PR: #3151
File: go/apps/api/openapi/gen.go:221-233
Timestamp: 2025-04-18T20:01:33.812Z
Learning: For destructive operations like deletion in the Unkey API, oneOf validation is preferred over anyOf to enforce explicit targeting and prevent ambiguity.
</retrieved_learning>

go/apps/api/openapi/gen.go (1)

<retrieved_learning>
Learnt from: chronark
PR: #3560
File: go/apps/api/routes/v2_keys_create_key/handler.go:468-581
Timestamp: 2025-07-15T14:47:20.490Z
Learning: In the Unkey codebase, role and permission names are validated at the OpenAPI schema layer with strict regex patterns: role names must match "^[a-zA-Z][a-zA-Z0-9_-]*$" (start with letter, followed by letters/numbers/underscores/hyphens) and permission names must match "^[a-zA-Z0-9_]+$" (letters, numbers, underscores only). This validation occurs during zen.BindBody call before handlers run, preventing malicious or improperly formatted names from reaching auto-creation logic.
</retrieved_learning>

🧬 Code Graph Analysis (7)
go/pkg/codes/generate.go (1)
go/pkg/codes/user_request.go (1)
  • User (19-23)
go/pkg/zen/middleware_errors.go (1)
go/pkg/codes/constants_gen.go (2)
  • UnkeyAuthErrorsAuthenticationMalformed (27-27)
  • UserErrorsBadRequestPermissionsQuerySyntaxError (16-16)
go/pkg/rbac/integration_test.go (2)
go/pkg/rbac/query.go (1)
  • T (84-90)
go/pkg/rbac/rbac.go (2)
  • New (23-25)
  • ParseQuery (156-158)
go/pkg/rbac/rbac.go (2)
go/pkg/codes/unkey_application.go (1)
  • App (60-80)
go/pkg/fault/wrap.go (2)
  • Internal (75-89)
  • Public (97-111)
go/apps/api/routes/v2_keys_verify_key/handler.go (3)
go/pkg/rbac/rbac.go (1)
  • ParseQuery (156-158)
go/pkg/codes/user_request.go (1)
  • User (19-23)
go/pkg/fault/wrap.go (1)
  • Internal (75-89)
go/pkg/rbac/lexer.go (3)
go/pkg/codes/user_request.go (1)
  • User (19-23)
go/pkg/codes/constants_gen.go (1)
  • URN (5-5)
go/pkg/fault/wrap.go (1)
  • Public (97-111)
go/pkg/rbac/parser.go (3)
go/pkg/rbac/query.go (3)
  • Or (64-70)
  • And (46-52)
  • S (100-106)
go/pkg/codes/user_request.go (1)
  • User (19-23)
go/pkg/fault/wrap.go (1)
  • Public (97-111)
🪛 LanguageTool
apps/docs/api-reference/errors-v2/user/bad_request/permissions_query_syntax_error.mdx

[grammar] ~27-~27: Use correct spacing
Context: ...r end" } ] } } ``` ## What Happened? This error occurs when the permissions q...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~29-~29: There might be a mistake here.
Context: ...ntax or characters. This can happen due to: - Invalid characters in permission name...

(QB_NEW_EN_OTHER)


[grammar] ~33-~33: Use correct spacing
Context: ...ssions** that don't follow the expected grammar ## Permissions Query Requirements The `ver...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~35-~35: Use correct spacing
Context: ... expected grammar ## Permissions Query Requirements The verifyKey endpoint accepts a permi...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~37-~37: There might be a mistake here.
Context: ...ons query string that must follow these rules: ### Valid Characters The query parser acce...

(QB_NEW_EN_OTHER)


[grammar] ~39-~39: Use correct spacing
Context: ...hat must follow these rules: ### Valid Characters The query parser accepts these character...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~41-~41: There might be a mistake here.
Context: ...racters The query parser accepts these characters: - Permissions: Must follow the permissi...

(QB_NEW_EN_OTHER)


[grammar] ~43-~43: There might be a mistake here.
Context: ...ormat (alphanumeric, dots, underscores, hyphens) - Letters: a-z, A-Z - Numbers:...

(QB_NEW_EN_OTHER)


[grammar] ~44-~44: There might be a mistake here.
Context: ... underscores, hyphens) - Letters: a-z, A-Z - Numbers: 0-9 - **Dot...

(QB_NEW_EN_OTHER)


[grammar] ~51-~51: Add a comma
Context: ...r parentheses - Whitespace: Spaces, tabs and new lines for separation (ignored b...

(QB_NEW_EN_OTHER_ERROR_IDS_22)


[grammar] ~51-~51: There might be a problem here.
Context: ...nd new lines for separation (ignored by parser) Everyhing else is not allowed. ### Query Structu...

(QB_NEW_EN_MERGED_MATCH)


[grammar] ~53-~53: Use correct spacing
Context: ...nored by parser) Everyhing else is not allowed. ### Query Structure A permissions query can...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~55-~55: Use correct spacing
Context: ...eryhing else is not allowed. ### Query Structure A permissions query can be: 1. **A sing...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~57-~57: There might be a mistake here.
Context: ...uery Structure A permissions query can be: 1. A single permission: permission_1 2....

(QB_NEW_EN_OTHER)


[grammar] ~62-~62: Use correct spacing
Context: ...mission_1 OR permission_24. **Grouped expressions**:(permission_1 OR permission_2) AND permission_3` Key rules: - Permission names must be ...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~65-~65: There might be a mistake here.
Context: ...s (letters, numbers, dots, underscores, hyphens) - Use AND when all permissions are requi...

(QB_NEW_EN_OTHER)


[grammar] ~66-~66: There might be a mistake here.
Context: ...s) - Use AND when all permissions are required - Use OR when any of the permissions is ...

(QB_NEW_EN_OTHER)


[grammar] ~67-~67: There might be a mistake here.
Context: ...Use OR when any of the permissions is sufficient - Use parentheses () to group expression...

(QB_NEW_EN_OTHER)


[grammar] ~68-~68: There might be a mistake here.
Context: ...s () to group expressions and control precedence - Operators are case insensitive: AND, `...

(QB_NEW_EN_OTHER)


[grammar] ~69-~69: Insert the missing word
Context: ... Operators are case insensitive: AND, AnD, and all work. ## Common Errors and S...

(QB_NEW_EN_OTHER_ERROR_IDS_32)


[grammar] ~69-~69: Use correct spacing
Context: ...se insensitive: AND, AnD, and all work. ## Common Errors and Solutions ### 1. Inva...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~71-~71: Use correct spacing
Context: ..., and all work. ## Common Errors and Solutions ### 1. Invalid Characters ```bash # ❌ Invali...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~73-~73: Use correct spacing
Context: ...on Errors and Solutions ### 1. Invalid Characters bash # ❌ Invalid - contains special characters curl -X POST https://api.unkey.com/v2/keys.verifyKey \ -H "Content-Type: application/json" \ -H "Authorization: Bearer unkey_XXXX" \ -d '{ "key": "sk_123", "permissions": "permission$1 OR permission@2" }' # ✅ Valid - use underscores or hyphens curl -X POST https://api.unkey.com/v2/keys.verifyKey \ -H "Content-Type: application/json" \ -H "Authorization: Bearer unkey_XXXX" \ -d '{ "key": "sk_123", "permissions": "permission_1 OR permission_2" }' ### 2. Missing Operands ```bash # ❌ Invalid ...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~94-~94: Use correct spacing
Context: ... permission_2" }' ### 2. Missing Operandsbash # ❌ Invalid - AND without right operand curl -X POST https://api.unkey.com/v2/keys.verifyKey \ -H "Content-Type: application/json" \ -H "Authorization: Bearer unkey_XXXX" \ -d '{ "key": "sk_123", "permissions": "permission_1 AND" }' # ✅ Valid - complete AND expression curl -X POST https://api.unkey.com/v2/keys.verifyKey \ -H "Content-Type: application/json" \ -H "Authorization: Bearer unkey_XXXX" \ -d '{ "key": "sk_123", "permissions": "permission_1 AND permission_2" }' ### 3. Unmatched Parenthesesbash # ❌ Inv...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~115-~115: Use correct spacing
Context: ...ermission_2" }' ### 3. Unmatched Parenthesesbash # ❌ Invalid - missing closing parenthesis curl -X POST https://api.unkey.com/v2/keys.verifyKey \ -H "Content-Type: application/json" \ -H "Authorization: Bearer unkey_XXXX" \ -d '{ "key": "sk_123", "permissions": "(permission_1 AND permission_2" }' # ✅ Valid - balanced parentheses curl -X POST https://api.unkey.com/v2/keys.verifyKey \ -H "Content-Type: application/json" \ -H "Authorization: Bearer unkey_XXXX" \ -d '{ "key": "sk_123", "permissions": "(permission_1 AND permission_2)" }' ### 4. Empty Parenthesesbash # ❌ Invalid...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~136-~136: Use correct spacing
Context: ...D permission_2)" }' ### 4. Empty Parenthesesbash # ❌ Invalid - empty parentheses curl -X POST https://api.unkey.com/v2/keys.verifyKey \ -H "Content-Type: application/json" \ -H "Authorization: Bearer unkey_XXXX" \ -d '{ "key": "sk_123", "permissions": "permission_1 AND ()" }' # ✅ Valid - parentheses with content curl -X POST https://api.unkey.com/v2/keys.verifyKey \ -H "Content-Type: application/json" \ -H "Authorization: Bearer unkey_XXXX" \ -d '{ "key": "sk_123", "permissions": "permission_1 AND (permission_2 OR permission_3)" }' ### 5. Incorrect Operator Placementbash ...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~157-~157: Use correct spacing
Context: ...3)" }' ### 5. Incorrect Operator Placementbash # ❌ Invalid - operator at start curl -X POST https://api.unkey.com/v2/keys.verifyKey \ -H "Content-Type: application/json" \ -H "Authorization: Bearer unkey_XXXX" \ -d '{ "key": "sk_123", "permissions": "OR permission_1" }' # ✅ Valid - operators between permissions curl -X POST https://api.unkey.com/v2/keys.verifyKey \ -H "Content-Type: application/json" \ -H "Authorization: Bearer unkey_XXXX" \ -d '{ "key": "sk_123", "permissions": "permission_1 OR permission_2" }' ``` ## Valid Query Examples ### Simple Permiss...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~178-~178: Use correct spacing
Context: ... permission_2" }' ## Valid Query Examples ### Simple Permissionbash curl -X POST h...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~180-~180: Use correct spacing
Context: ...`` ## Valid Query Examples ### Simple Permission bash curl -X POST https://api.unkey.com/v2/keys.verifyKey \ -H "Content-Type: application/json" \ -H "Authorization: Bearer unkey_XXXX" \ -d '{ "key": "sk_123", "permissions": "permission_1" }' ### AND Operation ```bash curl -X POST https...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~191-~191: Use correct spacing
Context: ...ions": "permission_1" }' ### AND Operationbash curl -X POST https://api.unkey.com/v2/keys.verifyKey \ -H "Content-Type: application/json" \ -H "Authorization: Bearer unkey_XXXX" \ -d '{ "key": "sk_123", "permissions": "permission_1 AND permission_2" }' ### OR Operationbash curl -X POST https:...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~202-~202: Use correct spacing
Context: ...on_1 AND permission_2" }' ### OR Operationbash curl -X POST https://api.unkey.com/v2/keys.verifyKey \ -H "Content-Type: application/json" \ -H "Authorization: Bearer unkey_XXXX" \ -d '{ "key": "sk_123", "permissions": "permission_1 OR permission_2" }' ### Complex Expressionsbash curl -X POST...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~213-~213: Use correct spacing
Context: ... OR permission_2" }' ### Complex Expressionsbash curl -X POST https://api.unkey.com/v2/keys.verifyKey \ -H "Content-Type: application/json" \ -H "Authorization: Bearer unkey_XXXX" \ -d '{ "key": "sk_123", "permissions": "(permission_1 OR permission_2) AND permission_3" }' ### Nested Expressionsbash curl -X POST ...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~224-~224: Use correct spacing
Context: ... AND permission_3" }' ### Nested Expressionsbash curl -X POST https://api.unkey.com/v2/keys.verifyKey \ -H "Content-Type: application/json" \ -H "Authorization: Bearer unkey_XXXX" \ -d '{ "key": "sk_123", "permissions": "((permission_1 OR permission_2) AND permission_3) OR permission_4" }' ``` ## Valid Permission Formats ### Simple Nam...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~235-~235: Use correct spacing
Context: ...ission_4" }' ``` ## Valid Permission Formats ### Simple Names - permission_1 - `user_re...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~237-~237: Use correct spacing
Context: ...## Valid Permission Formats ### Simple Names - permission_1 - user_read - admin-access ### Namespaced Permissions - `api.users.read...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~242-~242: Use correct spacing
Context: ..._read-admin-access ### Namespaced Permissions -api.users.read-billing.invoices.create-workspace.settings.update ### Mixed Formats -user_management.create`...

(QB_NEW_EN_OTHER_ERROR_IDS_5)


[grammar] ~247-~247: Use correct spacing
Context: ... workspace.settings.update ### Mixed Formats - user_management.create - billing-service.view - service123.feature_a.read ## Context This error is specific to the `...

(QB_NEW_EN_OTHER_ERROR_IDS_5)

🪛 YAMLlint (1.37.1)
go/apps/api/openapi/openapi.yaml

[warning] 3311-3311: too few spaces before comment: expected 2

(comments)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Test Agent Local / test_agent_local
  • GitHub Check: Test API / API Test Local
  • GitHub Check: Build / Build
  • GitHub Check: Test Packages / Test
🔇 Additional comments (32)
apps/docs/docs.json (1)

238-244: LGTM! Documentation structure follows existing patterns.

The addition of the "User" error group and the permissions query syntax error documentation page correctly follows the established error documentation hierarchy and aligns with the new SQL-like permissions query feature.

go/pkg/codes/unkey_application.go (3)

33-35: LGTM! Preparatory structure for limits-related errors.

The new appLimits struct follows the established pattern for error categorization and sets up the foundation for future limits and quota-related error codes.


53-55: LGTM! Consistent field addition.

The new Limits field properly integrates the limits error category into the overall application error structure.


79-79: LGTM! Proper initialization.

The Limits field is correctly initialized with an empty appLimits struct, maintaining consistency with the overall error code initialization pattern.

go/pkg/codes/generate.go (1)

49-49: LGTM! Proper integration of user error domain.

The new processErrorDomain call for the "User" system correctly integrates the newly introduced user error codes into the constant generation workflow. The placement before the Unkey domains is logical and maintains consistency.

go/pkg/zen/middleware_errors.go (1)

62-63: LGTM! Proper error handling for permissions query syntax.

The addition of codes.UserErrorsBadRequestPermissionsQuerySyntaxError to the Bad Request error handling case correctly ensures that permissions query syntax errors return HTTP 400 responses, which is appropriate for user input validation errors.

go/apps/api/routes/v2_keys_verify_key/400_test.go (1)

111-160: LGTM! Comprehensive test coverage for permissions query syntax validation.

The new test subtest provides excellent coverage for invalid permissions query syntax scenarios. The test cases are well-structured and validate:

  • Various syntax error types (missing operand, unmatched parenthesis, empty parentheses, operator at start)
  • Correct HTTP 400 response status
  • Proper error detail messages
  • Correct error type URL pointing to the documentation

The test structure follows the existing pattern and properly validates the new SQL-like permissions query feature introduced in this PR.

go/pkg/codes/constants_gen.go (2)

9-17: LGTM! Well-structured error code addition.

The new UserErrorsBadRequestPermissionsQuerySyntaxError constant follows the established naming conventions and URN format. The addition of the UserErrors section with BadRequest subsection provides a logical organization for user input validation errors.


127-128: Empty placeholder section added.

The "Limits" section appears to be a placeholder with no constants defined yet. This is acceptable for future expansion.

go/pkg/rbac/integration_test.go (3)

9-21: Excellent test coverage for simple query parsing and evaluation.

The integration test properly verifies that a simple permission query can be parsed and evaluated correctly. The test structure is clean and follows good testing practices.


23-34: Good coverage of complex query scenarios.

The test validates complex queries with AND/OR operators and parentheses, ensuring the parser and evaluator work together correctly for realistic permission scenarios.


57-67: Excellent precedence testing.

This test ensures that operator precedence is correctly handled (AND has higher precedence than OR), which is crucial for the SQL-like query syntax. The comment clearly explains the expected parsing behavior.

go/pkg/rbac/rbac.go (2)

122-127: Excellent improvement to error handling.

The replacement of a generic error with a structured fault provides better error classification and user-friendly messaging. Using the fault package with proper error codes aligns with the codebase's error handling patterns.


139-158: Well-documented and designed public API.

The ParseQuery function is excellently documented with:

  • Clear syntax rules and supported features
  • Practical examples showing various query formats
  • Explicit limits on query length and permission count
  • Good delegation to internal implementation

This provides a clean public interface for the SQL-like permission query parsing functionality.

go/pkg/rbac/parse_test.go (2)

9-67: Comprehensive test coverage for successful parsing scenarios.

The test cases cover all major parsing scenarios:

  • Simple permissions
  • Logical operators (AND, OR)
  • Operator precedence
  • Parentheses grouping

The use of helper functions like S(), And(), and Or() makes the expected results very readable and maintainable.


69-105: Excellent error case coverage.

The error tests validate specific failure scenarios with detailed error message checking:

  • Empty queries
  • Invalid characters
  • Incomplete expressions
  • Unmatched parentheses

This ensures robust error handling and helpful error messages for users.

go/apps/api/routes/v2_keys_verify_key/handler.go (1)

130-140: Excellent simplification of permissions handling.

The replacement of complex custom conversion logic with a direct call to rbac.ParseQuery significantly simplifies the code while providing better error handling. The structured fault wrapping with the appropriate error code ensures consistent error reporting for syntax errors.

This change aligns perfectly with the PR objective of introducing SQL-like query syntax for permissions.

go/pkg/rbac/doc.go (3)

10-13: Well-structured documentation introduction.

The introduction clearly differentiates between programmatic and SQL-like query construction methods, providing a good foundation for the detailed sections that follow.


22-62: Excellent programmatic usage example.

The documentation provides a complete, practical example that demonstrates the full workflow from RBAC setup to permission evaluation. The inclusion of error handling and result checking shows good practices.


64-101: Comprehensive SQL-like query documentation.

The documentation thoroughly covers the SQL-like query syntax with clear examples, precedence rules, and important limitations. The progression from simple to complex examples helps users understand the capabilities.

go/apps/api/routes/v2_keys_verify_key/200_test.go (3)

257-257: Excellent simplification of permission request construction.

The change from complex permission objects to simple string queries significantly improves code readability and maintainability while preserving functionality.

Also applies to: 282-282, 301-301


310-340: Good test coverage for complex permission queries.

The test correctly validates the AND operator functionality by creating a key with the required permissions and verifying the query evaluation works as expected.


342-394: Excellent scalability test for large permission queries.

This test provides crucial validation for the parser's ability to handle complex, real-world scenarios with multiple permissions and logical operators. The 25-permission test case ensures the system can scale appropriately.

go/pkg/codes/user_request.go (3)

3-7: Well-structured error definition.

The userBadRequest struct is cleanly defined with clear documentation explaining the purpose of the PermissionsQuerySyntaxError field.


9-14: Good hierarchical error organization.

The UserErrors struct provides a logical grouping for user-related errors, making them easy to find and reference throughout the application.


16-23: Proper error code instantiation.

The global User variable is correctly instantiated with the appropriate Code structure and follows consistent naming conventions. The documentation example helps developers understand how to reference these errors.

go/apps/api/openapi/openapi.yaml (1)

7260-7274: Example payload: confirm error.status type matches schema

In previous Unkey specs error.status was defined as a string ("400").
Here it’s provided as an integer (400). Make sure the example conforms to
#/components/schemas/ErrorObject to prevent breaking generated clients.

If the schema expects a string, the fix is:

-          status: 400
+          status: "400"

Also double-check that location: "body.permissions" follows the same path-format used elsewhere (some places use JSON-pointer /body/permissions).

go/pkg/rbac/lexer_test.go (1)

1-420: Comprehensive test coverage!

The lexer tests are well-structured with thorough coverage of tokenization scenarios, including edge cases, error handling, and various permission formats. The table-driven approach makes tests readable and maintainable.

apps/docs/api-reference/errors-v2/user/bad_request/permissions_query_syntax_error.mdx (1)

1-256: Well-structured and comprehensive error documentation!

The documentation provides clear explanations, practical examples, and actionable solutions for permission query syntax errors. The format with invalid/valid comparison examples is particularly helpful for users.

go/pkg/rbac/parser.go (1)

1-309: Well-implemented recursive descent parser!

The parser correctly implements operator precedence, provides detailed error messages with position information, and follows clean coding practices. The grammar documentation is clear and matches the implementation.

go/pkg/rbac/lexer.go (1)

1-296: Clean and efficient lexer implementation!

The lexer correctly handles tokenization with proper position tracking, case-insensitive operators, and intelligent word boundary detection. The isCompleteWord method elegantly solves the problem of distinguishing operators from permission identifiers containing operator substrings.

go/apps/api/openapi/gen.go (1)

1637-1644: Breaking change implemented correctly in generated code.

The simplification of the Permissions field from a complex union type to a simple string is properly implemented. The documentation clearly explains the new SQL-like query syntax with good examples.

Since this is auto-generated code from the OpenAPI specification, any issues should be addressed in the source specification rather than this file.

Note: This is a breaking API change that will require all clients to update their permission checking logic from the structured object approach to the new query string format.

chronark and others added 4 commits July 16, 2025 19:47
…_query_syntax_error.mdx

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
@graphite-app
Copy link

graphite-app bot commented Jul 17, 2025

Graphite Automations

"Notify author when CI fails" took an action on this PR • (07/17/25)

1 teammate was notified to this PR based on Andreas Thomas's automation.

"Post a GIF when PR approved" took an action on this PR • (07/17/25)

1 gif was posted to this PR based on Andreas Thomas's automation.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
go/apps/api/openapi/openapi.yaml (1)

3342-3342: YAML-lint spacing nit was already discussed

The single-space inline comment after maxLength: 1000 re-triggers YAML-lint’s “two-space” rule, but this exact nit was deemed non-blocking in earlier reviews.
Not flagging again.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b4258e1 and ce0e86d.

📒 Files selected for processing (2)
  • go/apps/api/openapi/gen.go (1 hunks)
  • go/apps/api/openapi/openapi.yaml (2 hunks)
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: chronark
PR: unkeyed/unkey#3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.242Z
Learning: In the Unkey API OpenAPI schema, the permissions query regex for the verifyKey endpoint intentionally allows all whitespace characters (including tabs and newlines) via `\s`. Do not flag this as an error in future reviews.
Learnt from: chronark
PR: unkeyed/unkey#3560
File: go/apps/api/routes/v2_keys_create_key/handler.go:468-581
Timestamp: 2025-07-15T14:47:20.490Z
Learning: In the Unkey codebase, role and permission names are validated at the OpenAPI schema layer with strict regex patterns: role names must match "^[a-zA-Z][a-zA-Z0-9_-]*$" (start with letter, followed by letters/numbers/underscores/hyphens) and permission names must match "^[a-zA-Z0-9_]+$" (letters, numbers, underscores only). This validation occurs during zen.BindBody call before handlers run, preventing malicious or improperly formatted names from reaching auto-creation logic.
Learnt from: chronark
PR: unkeyed/unkey#3560
File: go/apps/api/routes/v2_keys_create_key/handler.go:353-466
Timestamp: 2025-07-15T14:25:05.608Z
Learning: In the Unkey codebase, input validation for API endpoints is handled at the OpenAPI schema layer, which validates request fields like permission slugs (pattern: "^[a-zA-Z0-9_]+$", length: 1-100 characters) before requests reach the handler code. This validation occurs during the zen.BindBody call in handlers.
Learnt from: Flo4604
PR: unkeyed/unkey#2955
File: go/apps/api/routes/v2_identities_create_identity/handler.go:162-202
Timestamp: 2025-03-19T09:25:59.751Z
Learning: In the Unkey codebase, input validation for API endpoints is primarily handled through OpenAPI schema validation, which occurs before requests reach the handler code. For example, in the identities.createIdentity endpoint, minimum values for ratelimit duration and limit are defined in the OpenAPI schema rather than duplicating these checks in the handler.
Learnt from: chronark
PR: unkeyed/unkey#3420
File: go/pkg/hydra/store/gorm/gorm.go:486-498
Timestamp: 2025-07-02T11:51:58.572Z
Learning: The Hydra package (go/pkg/hydra) is planned to be migrated from GORM to sqlc for database operations, which explains why raw SQL queries are acceptable in the current implementation.
Learnt from: Flo4604
PR: unkeyed/unkey#3421
File: go/apps/api/openapi/openapi.yaml:196-200
Timestamp: 2025-07-03T05:58:10.699Z
Learning: In the Unkey codebase, OpenAPI 3.1 is used, which allows sibling keys (such as `description`) alongside `$ref` in schema objects. Do not flag this as an error in future reviews.
go/apps/api/openapi/openapi.yaml (10)

undefined

<retrieved_learning>
Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.242Z
Learning: In the Unkey API OpenAPI schema, the permissions query regex for the verifyKey endpoint intentionally allows all whitespace characters (including tabs and newlines) via \s. Do not flag this as an error in future reviews.
</retrieved_learning>

<retrieved_learning>
Learnt from: Flo4604
PR: #3421
File: go/apps/api/openapi/openapi.yaml:196-200
Timestamp: 2025-07-03T05:58:10.699Z
Learning: In the Unkey codebase, OpenAPI 3.1 is used, which allows sibling keys (such as description) alongside $ref in schema objects. Do not flag this as an error in future reviews.
</retrieved_learning>

<retrieved_learning>
Learnt from: chronark
PR: #3560
File: go/apps/api/routes/v2_keys_create_key/handler.go:353-466
Timestamp: 2025-07-15T14:25:05.608Z
Learning: In the Unkey codebase, input validation for API endpoints is handled at the OpenAPI schema layer, which validates request fields like permission slugs (pattern: "^[a-zA-Z0-9_]+$", length: 1-100 characters) before requests reach the handler code. This validation occurs during the zen.BindBody call in handlers.
</retrieved_learning>

<retrieved_learning>
Learnt from: chronark
PR: #3560
File: go/apps/api/routes/v2_keys_create_key/handler.go:468-581
Timestamp: 2025-07-15T14:47:20.490Z
Learning: In the Unkey codebase, role and permission names are validated at the OpenAPI schema layer with strict regex patterns: role names must match "^[a-zA-Z][a-zA-Z0-9_-]*$" (start with letter, followed by letters/numbers/underscores/hyphens) and permission names must match "^[a-zA-Z0-9_]+$" (letters, numbers, underscores only). This validation occurs during zen.BindBody call before handlers run, preventing malicious or improperly formatted names from reaching auto-creation logic.
</retrieved_learning>

<retrieved_learning>
Learnt from: Flo4604
PR: #2955
File: go/apps/api/routes/v2_identities_create_identity/handler.go:162-202
Timestamp: 2025-03-19T09:25:59.751Z
Learning: In the Unkey codebase, input validation for API endpoints is primarily handled through OpenAPI schema validation, which occurs before requests reach the handler code. For example, in the identities.createIdentity endpoint, minimum values for ratelimit duration and limit are defined in the OpenAPI schema rather than duplicating these checks in the handler.
</retrieved_learning>

<retrieved_learning>
Learnt from: Flo4604
PR: #3151
File: go/apps/api/openapi/gen.go:221-233
Timestamp: 2025-04-18T20:01:33.812Z
Learning: For destructive operations like deletion in the Unkey API, oneOf validation is preferred over anyOf to enforce explicit targeting and prevent ambiguity.
</retrieved_learning>

<retrieved_learning>
Learnt from: CR
PR: unkeyed/unkey#0
File: go/deploy/CLAUDE.md:0-0
Timestamp: 2025-07-09T08:42:29.316Z
Learning: Applies to go/deploy/**/*.{go,js,ts,tsx,py,sh,md,txt,json,yaml,yml,ini,rb,java,c,cpp,h,cs,rs,php,html,css,scss,xml} : Make sure to add relevant anchor comments whenever a file or piece of code is too complex, very important, confusing, or could have a bug.
</retrieved_learning>

<retrieved_learning>
Learnt from: CR
PR: unkeyed/unkey#0
File: go/deploy/CLAUDE.md:0-0
Timestamp: 2025-07-09T08:42:29.316Z
Learning: Applies to go/deploy/**/*.{go,js,ts,tsx,py,sh,md,txt,json,yaml,yml,ini,rb,java,c,cpp,h,cs,rs,php,html,css,scss,xml} : Use AIDEV-NOTE:, AIDEV-TODO:, AIDEV-BUSINESS_RULE:, or AIDEV-QUESTION: (all-caps prefix) as anchor comments aimed at AI and developers.
</retrieved_learning>

<retrieved_learning>
Learnt from: CR
PR: unkeyed/unkey#0
File: go/deploy/CLAUDE.md:0-0
Timestamp: 2025-07-09T08:42:29.316Z
Learning: Applies to go/deploy/**/*.{go,js,ts,tsx,py,sh,md,txt,json,yaml,yml,ini,rb,java,c,cpp,h,cs,rs,php,html,css,scss,xml} : Do not remove AIDEV-*s without explicit human instruction.
</retrieved_learning>

<retrieved_learning>
Learnt from: ogzhanolguncu
PR: #3321
File: apps/dashboard/lib/trpc/routers/authorization/roles/keys/schema-with-helpers.ts:5-8
Timestamp: 2025-06-18T12:28:10.449Z
Learning: In the unkey dashboard application, API validation for pagination limits is controlled at the UI level rather than requiring additional server-side validation, as the APIs are internal and protected by UI logic.
</retrieved_learning>

go/apps/api/openapi/gen.go (3)

<retrieved_learning>
Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.242Z
Learning: In the Unkey API OpenAPI schema, the permissions query regex for the verifyKey endpoint intentionally allows all whitespace characters (including tabs and newlines) via \s. Do not flag this as an error in future reviews.
</retrieved_learning>

<retrieved_learning>
Learnt from: chronark
PR: #3560
File: go/apps/api/routes/v2_keys_create_key/handler.go:468-581
Timestamp: 2025-07-15T14:47:20.490Z
Learning: In the Unkey codebase, role and permission names are validated at the OpenAPI schema layer with strict regex patterns: role names must match "^[a-zA-Z][a-zA-Z0-9_-]*$" (start with letter, followed by letters/numbers/underscores/hyphens) and permission names must match "^[a-zA-Z0-9_]+$" (letters, numbers, underscores only). This validation occurs during zen.BindBody call before handlers run, preventing malicious or improperly formatted names from reaching auto-creation logic.
</retrieved_learning>

<retrieved_learning>
Learnt from: chronark
PR: #3560
File: go/apps/api/routes/v2_keys_create_key/handler.go:353-466
Timestamp: 2025-07-15T14:25:05.608Z
Learning: In the Unkey codebase, input validation for API endpoints is handled at the OpenAPI schema layer, which validates request fields like permission slugs (pattern: "^[a-zA-Z0-9_]+$", length: 1-100 characters) before requests reach the handler code. This validation occurs during the zen.BindBody call in handlers.
</retrieved_learning>

🪛 YAMLlint (1.37.1)
go/apps/api/openapi/openapi.yaml

[warning] 3342-3342: too few spaces before comment: expected 2

(comments)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Test Go API Local / Test
  • GitHub Check: Test Agent Local / test_agent_local
  • GitHub Check: Test API / API Test Local
  • GitHub Check: Build / Build
  • GitHub Check: Test Packages / Test
  • GitHub Check: autofix
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (2)
go/apps/api/openapi/gen.go (1)

1638-1645: LGTM! The simplified permissions field correctly implements the new SQL-like query syntax.

The change from a complex union type to a simple string field with clear documentation examples aligns perfectly with the PR objectives. The comment provides excellent guidance on the supported query syntax including single permissions, logical operators (AND, OR), and parentheses for grouping.

go/apps/api/openapi/openapi.yaml (1)

3340-3351: Permissions field definition looks solid and aligns with the new parser

The switch to a plain string with generous maxLength and an inclusive pattern is consistent with the lexer/parser that landed in pkg/rbac. The examples and description give SDK generators everything they need, and the regex correctly allows \s for multi-line queries per the previous decision.
No issues from my side.

@chronark chronark added this pull request to the merge queue Jul 17, 2025
@graphite-app
Copy link

graphite-app bot commented Jul 17, 2025

SpongeBob SquarePants gif. SpongeBob smiles and gives two thumbs up, and then a dozen more hands giving thumbs up bud out from all sides of his body. Text, 'Many thumbs up.' (Added via Giphy)

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.

3 participants