Skip to content

feat: allow either permission id or slug in permission related requests. #3653

Merged
chronark merged 24 commits intomainfrom
feat-allow-for-permission-slug+id
Jul 28, 2025
Merged

feat: allow either permission id or slug in permission related requests. #3653
chronark merged 24 commits intomainfrom
feat-allow-for-permission-slug+id

Conversation

@Flo4604
Copy link
Member

@Flo4604 Flo4604 commented Jul 23, 2025

What does this PR do?

This updates the permission endpoints to take in either a permissionID or a slug.

When setting or adding permissions, permissions that dont exist will be auto created if the root key has permissions for it.
keyRole endpoints only allow for slugs and not both ids and slugs.

The keyendpoints themselves allow for both.

The operations will fail with a 403 error otherwise.

Also add an unified emptyResponse

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 A
  • Test B

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

    • Simplified API requests for managing permissions and roles: accept lists of strings (slugs or names) instead of complex objects.
    • Unified empty success responses across endpoints for consistency.
    • Enhanced role and permission details in responses with standardized schemas.
    • Added batch operations for permission and role assignments to improve efficiency.
  • Bug Fixes

    • Improved validation and error messages for permission and role operations.
    • Fixed permission and role lookup to support ID or slug/name with workspace scoping.
  • Refactor

    • Streamlined internal logic for adding, removing, and setting permissions and roles with bulk queries and batch inserts/deletes.
    • Replaced multiple queries with consolidated queries returning roles with permissions as JSON.
    • Updated audit logging to use standardized event and resource type constants.
    • Replaced deprecated timestamp fields and simplified schema constraints.
  • Documentation

    • Updated API documentation to clarify request/response formats, validation rules, and permission/role identification.
  • Tests

    • Simplified and updated test cases to match new request formats and improved coverage.
    • Removed redundant tests for deprecated request structures and validation scenarios.
    • Updated test data to use new permission and role ID/name conventions and custom nullable string types.

@changeset-bot
Copy link

changeset-bot bot commented Jul 23, 2025

⚠️ No Changeset found

Latest commit: f237276

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

@vercel
Copy link

vercel bot commented Jul 23, 2025

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

2 Skipped Deployments
Name Status Preview Comments Updated (UTC)
dashboard ⬜️ Ignored (Inspect) Visit Preview Jul 28, 2025 3:11pm
engineering ⬜️ Ignored (Inspect) Visit Preview Jul 28, 2025 3:11pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jul 23, 2025

📝 Walkthrough

Walkthrough

This change is a comprehensive refactor and standardization of the API's permissions and roles management. It unifies empty response types, simplifies request and response schemas by using string lists instead of complex objects, and streamlines handler logic and database operations for permissions and roles. The update also introduces new batch database queries and enhances test coverage to align with the new models.

Changes

Cohort / File(s) Change Summary
API Model & OpenAPI Spec Standardization
go/apps/api/openapi/gen.go, go/apps/api/openapi/openapi-generated.yaml, go/apps/api/openapi/spec/common/EmptyResponse.yaml, go/apps/api/openapi/spec/common/permission.yaml, go/apps/api/openapi/spec/common/role.yaml, go/apps/api/openapi/spec/common/KeyResponseData.yaml, go/apps/api/openapi/spec/paths/v2/...
Unified empty response types (EmptyResponse), removed timestamp fields from Permission and Role, simplified request/response bodies for permissions/roles to use string slices, updated OpenAPI schemas and validation patterns, and centralized permission/role object definitions.
Handlers: Permissions & Roles Management
go/apps/api/routes/v2_keys_add_permissions/handler.go, go/apps/api/routes/v2_keys_remove_permissions/handler.go, go/apps/api/routes/v2_keys_set_permissions/handler.go, go/apps/api/routes/v2_keys_add_roles/handler.go, go/apps/api/routes/v2_keys_remove_roles/handler.go, go/apps/api/routes/v2_keys_set_roles/handler.go, go/apps/api/routes/v2_keys_update_key/handler.go, go/apps/api/routes/v2_keys_create_key/handler.go, go/apps/api/routes/v2_keys_delete_key/handler.go, go/apps/api/routes/v2_keys_update_credits/handler.go, go/apps/api/routes/v2_permissions_create_permission/handler.go, go/apps/api/routes/v2_permissions_create_role/handler.go, go/apps/api/routes/v2_permissions_delete_permission/handler.go, go/apps/api/routes/v2_permissions_delete_role/handler.go, go/apps/api/routes/v2_permissions_get_permission/handler.go, go/apps/api/routes/v2_permissions_get_role/handler.go, go/apps/api/routes/v2_permissions_list_permissions/handler.go, go/apps/api/routes/v2_permissions_list_roles/handler.go, go/apps/api/routes/v2_apis_list_keys/handler.go
Refactored handlers to use new schemas, batch queries for permissions/roles, bulk insert/delete operations, improved audit logging, and streamlined response construction.
Test Suite Updates: Permissions & Roles
go/apps/api/routes/v2_keys_add_permissions/*_test.go, go/apps/api/routes/v2_keys_remove_permissions/*_test.go, go/apps/api/routes/v2_keys_set_permissions/*_test.go, go/apps/api/routes/v2_keys_add_roles/*_test.go, go/apps/api/routes/v2_keys_remove_roles/*_test.go, go/apps/api/routes/v2_keys_set_roles/*_test.go, go/apps/api/routes/v2_permissions_delete_permission/*_test.go, go/apps/api/routes/v2_permissions_delete_role/*_test.go, go/apps/api/routes/v2_permissions_get_permission/*_test.go, go/apps/api/routes/v2_permissions_get_role/*_test.go, go/apps/api/routes/v2_permissions_list_permissions/*_test.go, go/apps/api/routes/v2_permissions_list_roles/*_test.go
Updated tests to match simplified request/response schemas, removed tests for deprecated formats, adjusted assertions and test data, and refactored to use new types and helper methods.
Database Layer: Queries, Models, Plugins
go/pkg/db/models_generated.go, go/pkg/db/permission_find_by_id_or_slug.sql_generated.go, go/pkg/db/permission_find_by_slugs.sql_generated.go, go/pkg/db/key_permission_insert.sql_generated.go, go/pkg/db/key_permission_delete_many_by_key_and_permission_ids.sql_generated.go, go/pkg/db/key_role_delete_many_by_key_and_role_ids.sql_generated.go, go/pkg/db/role_find_by_id_or_name_with_perms.sql_generated.go, go/pkg/db/role_find_many_by_id_or_name_with_perms.sql_generated.go, go/pkg/db/role_find_many_by_name_with_perms.sql_generated.go, go/pkg/db/role_list.sql_generated.go, go/pkg/db/role_list_by_key_id.sql_generated.go, go/pkg/db/permission_insert.sql_generated.go, go/pkg/db/querier_generated.go, go/pkg/db/plugins/bulk-insert/*, go/pkg/db/sqlc.json, go/pkg/db/types/null_string.go
Added new batch query methods for permissions/roles, updated models to use dbtype.NullString, enhanced bulk insert plugin to support ON DUPLICATE KEY UPDATE, added new SQL queries for batch operations, and updated code generation configs.
Seed Utilities & Middleware
go/pkg/testutil/seed/seed.go, go/pkg/zen/middleware_tracing.go
Updated seed utilities to use new types, added request ID tracing to middleware.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant API_Handler
    participant DB
    participant AuditLog

    Client->>API_Handler: Add/Remove/Set Permissions/Roles (with string lists)
    API_Handler->>DB: Batch lookup permissions/roles by slugs/names
    alt Missing permissions/roles
        API_Handler->>DB: Create missing permissions (if authorized)
        API_Handler->>AuditLog: Log permission creation
    end
    API_Handler->>DB: Bulk insert/delete key-permission or key-role associations
    API_Handler->>AuditLog: Log assignment/removal
    API_Handler->>DB: Invalidate key cache
    API_Handler-->>Client: Response with updated permissions/roles (standardized format)
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~90+ minutes

Possibly related PRs

  • unkeyed/unkey#3421: Directly related; both PRs modify go/apps/api/openapi/gen.go and standardize API model definitions for permissions and roles.
  • unkeyed/unkey#3631: Related; introduces the bulk insert plugin for sqlc, which this PR leverages for batch operations in permissions/roles management.
  • unkeyed/unkey#3561: Related; both PRs refactor and standardize permissions and roles API models, focusing on request/response schema simplification.

Suggested labels

Core Team

Suggested reviewers

  • perkinsjr
  • mcstepp
  • MichaelUnkey
  • ogzhanolguncu
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat-allow-for-permission-slug+id

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 generate unit tests to generate unit tests for 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

make it a draft if you don’t want merges 🙂

Copy link
Collaborator

also note to self on that one 😄

Copy link
Member Author

Flo4604 commented Jul 24, 2025

fair enough

@Flo4604 Flo4604 marked this pull request as draft July 24, 2025 14:10
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: 14

🔭 Outside diff range comments (4)
go/apps/api/routes/v2_permissions_delete_permission/400_test.go (1)

38-39: Update test case names and comments for consistency.

The test case names and comments still reference "permissionId" but the actual field is now "Permission". Consider updating for consistency.

- // Test case for missing required permissionId
- t.Run("missing permissionId", func(t *testing.T) {
+ // Test case for missing required permission
+ t.Run("missing permission", func(t *testing.T) {
- // Test case for empty permissionId
- t.Run("empty permissionId", func(t *testing.T) {
+ // Test case for empty permission
+ t.Run("empty permission", func(t *testing.T) {

Also applies to: 57-58

go/apps/api/openapi/spec/paths/v2/permissions/getPermission/V2PermissionsGetPermissionRequestBody.yaml (1)

19-19: Fix inconsistent field name in example.

The example still uses the old field name permissionId but should use permission to match the updated schema.

-      permissionId: perm_1234567890abcdef
+      permission: perm_1234567890abcdef
go/apps/api/routes/v2_permissions_delete_permission/handler.go (1)

111-133: Consider audit log field consistency.

The audit log uses permission.ID for display but permission.Slug for the resource name. While functionally correct, this mixed usage might cause confusion during audit review.

Consider standardizing to use either ID or slug consistently, or include both for clarity:

-				Display:     "Deleted " + permission.ID,
+				Display:     "Deleted permission " + permission.Slug,
go/apps/api/routes/v2_keys_update_key/handler.go (1)

91-106: Address the TODO: Implement proper permission checks for role operations.

The TODO comment correctly identifies that the current permission check is insufficient. Users with UpdateKey permission can modify roles without specific role management permissions, which could lead to privilege escalation.

Would you like me to implement the proper permission checks for role operations or create an issue to track this security gap?

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 8eab750 and a54bac8.

📒 Files selected for processing (78)
  • go/apps/api/openapi/gen.go (13 hunks)
  • go/apps/api/openapi/openapi-generated.yaml (15 hunks)
  • go/apps/api/openapi/spec/common/EmptyResponse.yaml (1 hunks)
  • go/apps/api/openapi/spec/common/KeyResponseData.yaml (1 hunks)
  • go/apps/api/openapi/spec/common/permission.yaml (1 hunks)
  • go/apps/api/openapi/spec/paths/v2/apis/deleteApi/V2ApisDeleteApiResponseBody.yaml (1 hunks)
  • go/apps/api/openapi/spec/paths/v2/keys/addPermissions/V2KeysAddPermissionsRequestBody.yaml (1 hunks)
  • go/apps/api/openapi/spec/paths/v2/keys/addPermissions/V2KeysAddPermissionsResponseData.yaml (1 hunks)
  • go/apps/api/openapi/spec/paths/v2/keys/deleteKey/V2KeysDeleteKeyResponseBody.yaml (1 hunks)
  • go/apps/api/openapi/spec/paths/v2/keys/removePermissions/V2KeysRemovePermissionsRequestBody.yaml (1 hunks)
  • go/apps/api/openapi/spec/paths/v2/keys/removePermissions/V2KeysRemovePermissionsResponseData.yaml (1 hunks)
  • go/apps/api/openapi/spec/paths/v2/keys/setPermissions/V2KeysSetPermissionsRequestBody.yaml (1 hunks)
  • go/apps/api/openapi/spec/paths/v2/keys/setPermissions/V2KeysSetPermissionsResponseData.yaml (1 hunks)
  • go/apps/api/openapi/spec/paths/v2/keys/updateKey/V2KeysUpdateKeyResponseBody.yaml (1 hunks)
  • go/apps/api/openapi/spec/paths/v2/keys/updateKey/V2KeysUpdateKeyResponseData.yaml (0 hunks)
  • go/apps/api/openapi/spec/paths/v2/permissions/deletePermission/V2PermissionsDeletePermissionRequestBody.yaml (1 hunks)
  • go/apps/api/openapi/spec/paths/v2/permissions/deletePermission/V2PermissionsDeletePermissionResponseBody.yaml (1 hunks)
  • go/apps/api/openapi/spec/paths/v2/permissions/deleteRole/V2PermissionsDeleteRoleResponseBody.yaml (1 hunks)
  • go/apps/api/openapi/spec/paths/v2/permissions/getPermission/V2PermissionsGetPermissionRequestBody.yaml (1 hunks)
  • go/apps/api/routes/v2_keys_add_permissions/200_test.go (7 hunks)
  • go/apps/api/routes/v2_keys_add_permissions/400_test.go (5 hunks)
  • go/apps/api/routes/v2_keys_add_permissions/401_test.go (3 hunks)
  • go/apps/api/routes/v2_keys_add_permissions/403_test.go (4 hunks)
  • go/apps/api/routes/v2_keys_add_permissions/404_test.go (4 hunks)
  • go/apps/api/routes/v2_keys_add_permissions/handler.go (6 hunks)
  • go/apps/api/routes/v2_keys_add_roles/handler.go (1 hunks)
  • go/apps/api/routes/v2_keys_create_key/handler.go (3 hunks)
  • go/apps/api/routes/v2_keys_delete_key/handler.go (1 hunks)
  • go/apps/api/routes/v2_keys_remove_permissions/200_test.go (7 hunks)
  • go/apps/api/routes/v2_keys_remove_permissions/400_test.go (1 hunks)
  • go/apps/api/routes/v2_keys_remove_permissions/401_test.go (1 hunks)
  • go/apps/api/routes/v2_keys_remove_permissions/403_test.go (2 hunks)
  • go/apps/api/routes/v2_keys_remove_permissions/404_test.go (5 hunks)
  • go/apps/api/routes/v2_keys_remove_permissions/handler.go (5 hunks)
  • go/apps/api/routes/v2_keys_remove_roles/handler.go (1 hunks)
  • go/apps/api/routes/v2_keys_set_permissions/200_test.go (7 hunks)
  • go/apps/api/routes/v2_keys_set_permissions/400_test.go (1 hunks)
  • go/apps/api/routes/v2_keys_set_permissions/401_test.go (1 hunks)
  • go/apps/api/routes/v2_keys_set_permissions/403_test.go (4 hunks)
  • go/apps/api/routes/v2_keys_set_permissions/404_test.go (7 hunks)
  • go/apps/api/routes/v2_keys_set_permissions/handler.go (6 hunks)
  • go/apps/api/routes/v2_keys_set_roles/handler.go (2 hunks)
  • go/apps/api/routes/v2_keys_update_credits/handler.go (0 hunks)
  • go/apps/api/routes/v2_keys_update_key/handler.go (6 hunks)
  • go/apps/api/routes/v2_permissions_create_permission/handler.go (2 hunks)
  • go/apps/api/routes/v2_permissions_create_role/handler.go (2 hunks)
  • go/apps/api/routes/v2_permissions_delete_permission/200_test.go (2 hunks)
  • go/apps/api/routes/v2_permissions_delete_permission/400_test.go (1 hunks)
  • go/apps/api/routes/v2_permissions_delete_permission/401_test.go (1 hunks)
  • go/apps/api/routes/v2_permissions_delete_permission/403_test.go (2 hunks)
  • go/apps/api/routes/v2_permissions_delete_permission/404_test.go (3 hunks)
  • go/apps/api/routes/v2_permissions_delete_permission/handler.go (1 hunks)
  • go/apps/api/routes/v2_permissions_delete_role/handler.go (2 hunks)
  • go/apps/api/routes/v2_permissions_get_permission/200_test.go (3 hunks)
  • go/apps/api/routes/v2_permissions_get_permission/400_test.go (2 hunks)
  • go/apps/api/routes/v2_permissions_get_permission/401_test.go (1 hunks)
  • go/apps/api/routes/v2_permissions_get_permission/403_test.go (2 hunks)
  • go/apps/api/routes/v2_permissions_get_permission/404_test.go (2 hunks)
  • go/apps/api/routes/v2_permissions_get_permission/handler.go (1 hunks)
  • go/apps/api/routes/v2_permissions_get_role/handler.go (0 hunks)
  • go/apps/api/routes/v2_permissions_list_permissions/handler.go (0 hunks)
  • go/apps/api/routes/v2_permissions_list_roles/handler.go (0 hunks)
  • go/pkg/db/bulk_key_permission_insert.sql.go (2 hunks)
  • go/pkg/db/bulk_ratelimit_override_insert.sql.go (1 hunks)
  • go/pkg/db/key_permission_delete_many_by_key_and_permission_ids.sql_generated.go (1 hunks)
  • go/pkg/db/key_permission_insert.sql_generated.go (3 hunks)
  • go/pkg/db/permission_find_by_id_or_slug.sql_generated.go (1 hunks)
  • go/pkg/db/permission_find_many_by_id_or_slug.sql_generated.go (1 hunks)
  • go/pkg/db/plugins/bulk-insert/bulk_insert.go.tmpl (1 hunks)
  • go/pkg/db/plugins/bulk-insert/generator.go (3 hunks)
  • go/pkg/db/plugins/bulk-insert/parser.go (3 hunks)
  • go/pkg/db/plugins/bulk-insert/template.go (1 hunks)
  • go/pkg/db/querier_generated.go (4 hunks)
  • go/pkg/db/queries/key_permission_delete_many_by_key_and_permission_ids.sql (1 hunks)
  • go/pkg/db/queries/key_permission_insert.sql (1 hunks)
  • go/pkg/db/queries/permission_find_by_id_or_slug.sql (1 hunks)
  • go/pkg/db/queries/permission_find_many_by_id_or_slug.sql (1 hunks)
  • go/pkg/zen/middleware_tracing.go (2 hunks)
💤 Files with no reviewable changes (5)
  • go/apps/api/routes/v2_permissions_list_roles/handler.go
  • go/apps/api/routes/v2_permissions_get_role/handler.go
  • go/apps/api/openapi/spec/paths/v2/keys/updateKey/V2KeysUpdateKeyResponseData.yaml
  • go/apps/api/routes/v2_keys_update_credits/handler.go
  • go/apps/api/routes/v2_permissions_list_permissions/handler.go
🧰 Additional context used
🧠 Learnings (52)
📓 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.
go/apps/api/openapi/spec/common/KeyResponseData.yaml (4)

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: #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: #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.

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.

go/apps/api/routes/v2_keys_remove_permissions/200_test.go (5)

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: #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: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: When querying or updating namespaces in the Unkey dashboard, always scope the operations to the current workspace using eq(table.workspaceId, ctx.workspace.id) to prevent cross-workspace access.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use ctx.workspace.id directly instead of fetching the workspace separately for better performance and security.

go/apps/api/routes/v2_permissions_delete_role/handler.go (2)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

Learnt from: chronark
PR: #2146
File: apps/dashboard/lib/trpc/routers/api/setDefaultPrefix.ts:80-80
Timestamp: 2024-10-04T17:27:08.666Z
Learning: Ensure that audit log descriptions accurately reflect the action being performed, such as updating the defaultPrefix, and avoid incorrect references like 'name' when not applicable.

go/apps/api/routes/v2_permissions_delete_permission/400_test.go (2)

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.

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.

go/apps/api/routes/v2_keys_add_roles/handler.go (2)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

Learnt from: chronark
PR: #2146
File: apps/dashboard/lib/trpc/routers/api/setDefaultPrefix.ts:80-80
Timestamp: 2024-10-04T17:27:08.666Z
Learning: Ensure that audit log descriptions accurately reflect the action being performed, such as updating the defaultPrefix, and avoid incorrect references like 'name' when not applicable.

go/apps/api/openapi/spec/paths/v2/keys/deleteKey/V2KeysDeleteKeyResponseBody.yaml (1)

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.

go/apps/api/routes/v2_keys_set_permissions/401_test.go (1)

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.

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

Learnt from: chronark
PR: #2146
File: apps/dashboard/lib/trpc/routers/api/setDefaultPrefix.ts:80-80
Timestamp: 2024-10-04T17:27:08.666Z
Learning: Ensure that audit log descriptions accurately reflect the action being performed, such as updating the defaultPrefix, and avoid incorrect references like 'name' when not applicable.

go/apps/api/openapi/spec/paths/v2/permissions/getPermission/V2PermissionsGetPermissionRequestBody.yaml (2)

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.

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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.

go/apps/api/openapi/spec/paths/v2/keys/removePermissions/V2KeysRemovePermissionsRequestBody.yaml (5)

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: #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: #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: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

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.

go/pkg/db/queries/key_permission_delete_many_by_key_and_permission_ids.sql (1)

Learnt from: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

go/apps/api/openapi/spec/paths/v2/keys/removePermissions/V2KeysRemovePermissionsResponseData.yaml (2)

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.

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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.

go/apps/api/routes/v2_permissions_create_role/handler.go (2)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

Learnt from: chronark
PR: #2146
File: apps/dashboard/lib/trpc/routers/api/setDefaultPrefix.ts:80-80
Timestamp: 2024-10-04T17:27:08.666Z
Learning: Ensure that audit log descriptions accurately reflect the action being performed, such as updating the defaultPrefix, and avoid incorrect references like 'name' when not applicable.

go/apps/api/routes/v2_keys_add_permissions/400_test.go (7)

Learnt from: MichaelUnkey
PR: #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: #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: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: #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: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: When querying or updating namespaces in the Unkey dashboard, always scope the operations to the current workspace using eq(table.workspaceId, ctx.workspace.id) to prevent cross-workspace access.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use ctx.workspace.id directly instead of fetching the workspace separately for better performance and security.

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.

go/pkg/db/queries/key_permission_insert.sql (4)

Learnt from: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

Learnt from: chronark
PR: #3161
File: go/pkg/clickhouse/schema/databases/002_ratelimits/006_ratelimits_per_day_v1.sql:1-13
Timestamp: 2025-04-22T14:43:11.724Z
Learning: In the unkey project, the SQL files in clickhouse/schema/databases represent the current production schema and shouldn't be modified directly in PRs. Schema changes require dedicated migration scripts.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use ctx.workspace.id directly instead of fetching the workspace separately for better performance and security.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: When querying or updating namespaces in the Unkey dashboard, always scope the operations to the current workspace using eq(table.workspaceId, ctx.workspace.id) to prevent cross-workspace access.

go/apps/api/routes/v2_keys_set_permissions/400_test.go (5)

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.

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.

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: 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.

Learnt from: MichaelUnkey
PR: #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.

go/apps/api/routes/v2_keys_set_roles/handler.go (3)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

Learnt from: chronark
PR: #2146
File: apps/dashboard/lib/trpc/routers/api/setDefaultPrefix.ts:80-80
Timestamp: 2024-10-04T17:27:08.666Z
Learning: Ensure that audit log descriptions accurately reflect the action being performed, such as updating the defaultPrefix, and avoid incorrect references like 'name' when not applicable.

Learnt from: chronark
PR: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

go/apps/api/routes/v2_keys_add_permissions/401_test.go (5)

Learnt from: MichaelUnkey
PR: #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: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use ctx.workspace.id directly instead of fetching the workspace separately for better performance and security.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: When querying or updating namespaces in the Unkey dashboard, always scope the operations to the current workspace using eq(table.workspaceId, ctx.workspace.id) to prevent cross-workspace access.

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.

go/pkg/db/key_permission_insert.sql_generated.go (3)

Learnt from: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

Learnt from: Flo4604
PR: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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.

Learnt from: chronark
PR: #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_permissions_get_permission/400_test.go (2)

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.

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.

go/apps/api/routes/v2_keys_set_permissions/200_test.go (7)

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.

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: #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: MichaelUnkey
PR: #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: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: When querying or updating namespaces in the Unkey dashboard, always scope the operations to the current workspace using eq(table.workspaceId, ctx.workspace.id) to prevent cross-workspace access.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use ctx.workspace.id directly instead of fetching the workspace separately for better performance and security.

go/pkg/db/plugins/bulk-insert/template.go (1)

Learnt from: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

go/pkg/db/bulk_key_permission_insert.sql.go (2)

Learnt from: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

Learnt from: Flo4604
PR: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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/apps/api/routes/v2_permissions_get_permission/handler.go (1)

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use ctx.workspace.id directly instead of fetching the workspace separately for better performance and security.

go/apps/api/openapi/spec/paths/v2/permissions/deletePermission/V2PermissionsDeletePermissionRequestBody.yaml (3)

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.

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.

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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.

go/apps/api/routes/v2_keys_add_permissions/403_test.go (5)

Learnt from: MichaelUnkey
PR: #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: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use ctx.workspace.id directly instead of fetching the workspace separately for better performance and security.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: When querying or updating namespaces in the Unkey dashboard, always scope the operations to the current workspace using eq(table.workspaceId, ctx.workspace.id) to prevent cross-workspace access.

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.

go/apps/api/routes/v2_keys_add_permissions/200_test.go (5)

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: MichaelUnkey
PR: #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: #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: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: When querying or updating namespaces in the Unkey dashboard, always scope the operations to the current workspace using eq(table.workspaceId, ctx.workspace.id) to prevent cross-workspace access.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use ctx.workspace.id directly instead of fetching the workspace separately for better performance and security.

go/apps/api/routes/v2_keys_remove_permissions/400_test.go (4)

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.

Learnt from: MichaelUnkey
PR: #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: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: #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/apps/api/openapi/spec/paths/v2/keys/addPermissions/V2KeysAddPermissionsResponseData.yaml (2)

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.

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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.

go/apps/api/routes/v2_keys_update_key/handler.go (8)

Learnt from: chronark
PR: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

Learnt from: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

Learnt from: MichaelUnkey
PR: #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: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: #2146
File: apps/dashboard/lib/trpc/routers/api/setDefaultPrefix.ts:80-80
Timestamp: 2024-10-04T17:27:08.666Z
Learning: Ensure that audit log descriptions accurately reflect the action being performed, such as updating the defaultPrefix, and avoid incorrect references like 'name' when not applicable.

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.

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.

Learnt from: Flo4604
PR: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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/apps/api/routes/v2_keys_remove_permissions/403_test.go (8)

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use ctx.workspace.id directly instead of fetching the workspace separately for better performance and security.

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

Learnt from: Flo4604
PR: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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.

Learnt from: chronark
PR: #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: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

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.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: When querying or updating namespaces in the Unkey dashboard, always scope the operations to the current workspace using eq(table.workspaceId, ctx.workspace.id) to prevent cross-workspace access.

go/pkg/db/plugins/bulk-insert/parser.go (1)

Learnt from: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

go/apps/api/routes/v2_keys_remove_permissions/404_test.go (5)

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: #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: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: When querying or updating namespaces in the Unkey dashboard, always scope the operations to the current workspace using eq(table.workspaceId, ctx.workspace.id) to prevent cross-workspace access.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use ctx.workspace.id directly instead of fetching the workspace separately for better performance and security.

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.

go/apps/api/openapi/spec/paths/v2/keys/updateKey/V2KeysUpdateKeyResponseBody.yaml (2)

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.

Learnt from: MichaelUnkey
PR: #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.

go/pkg/db/plugins/bulk-insert/bulk_insert.go.tmpl (1)

Learnt from: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

go/apps/api/openapi/spec/paths/v2/keys/setPermissions/V2KeysSetPermissionsRequestBody.yaml (6)

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: #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: #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: #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.

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.

Learnt from: chronark
PR: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

go/pkg/db/bulk_ratelimit_override_insert.sql.go (1)

Learnt from: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

go/apps/api/openapi/spec/paths/v2/keys/addPermissions/V2KeysAddPermissionsRequestBody.yaml (5)

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: #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: #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: #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.

Learnt from: chronark
PR: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

go/pkg/db/plugins/bulk-insert/generator.go (1)

Learnt from: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

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

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

go/apps/api/routes/v2_keys_set_permissions/403_test.go (4)

Learnt from: MichaelUnkey
PR: #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: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use ctx.workspace.id directly instead of fetching the workspace separately for better performance and security.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: When querying or updating namespaces in the Unkey dashboard, always scope the operations to the current workspace using eq(table.workspaceId, ctx.workspace.id) to prevent cross-workspace access.

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.

go/pkg/db/key_permission_delete_many_by_key_and_permission_ids.sql_generated.go (2)

Learnt from: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

Learnt from: chronark
PR: #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_set_permissions/404_test.go (8)

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: #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: MichaelUnkey
PR: #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: #2126
File: apps/api/src/routes/v1_ratelimit_getOverride.happy.test.ts:36-36
Timestamp: 2024-11-13T19:06:36.786Z
Learning: In the rate limit test files (e.g., apps/api/src/routes/v1_ratelimit_getOverride.happy.test.ts), URL parameters like namespaceId and identifier do not need to be URL-encoded in the test code because the values used are always considered safe within the test environment.

Learnt from: chronark
PR: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: When querying or updating namespaces in the Unkey dashboard, always scope the operations to the current workspace using eq(table.workspaceId, ctx.workspace.id) to prevent cross-workspace access.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use ctx.workspace.id directly instead of fetching the workspace separately for better performance and security.

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.

go/apps/api/openapi/spec/paths/v2/keys/setPermissions/V2KeysSetPermissionsResponseData.yaml (2)

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.

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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.

go/apps/api/routes/v2_keys_add_permissions/404_test.go (5)

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: When querying or updating namespaces in the Unkey dashboard, always scope the operations to the current workspace using eq(table.workspaceId, ctx.workspace.id) to prevent cross-workspace access.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use ctx.workspace.id directly instead of fetching the workspace separately for better performance and security.

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.

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.

go/apps/api/routes/v2_keys_remove_permissions/handler.go (6)

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.

Learnt from: chronark
PR: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

Learnt from: Flo4604
PR: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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.

Learnt from: chronark
PR: #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: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use ctx.workspace.id directly instead of fetching the workspace separately for better performance and security.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: When querying or updating namespaces in the Unkey dashboard, always scope the operations to the current workspace using eq(table.workspaceId, ctx.workspace.id) to prevent cross-workspace access.

go/pkg/db/querier_generated.go (1)

Learnt from: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

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

Learnt from: chronark
PR: #2146
File: apps/dashboard/lib/trpc/routers/api/setDefaultPrefix.ts:80-80
Timestamp: 2024-10-04T17:27:08.666Z
Learning: Ensure that audit log descriptions accurately reflect the action being performed, such as updating the defaultPrefix, and avoid incorrect references like 'name' when not applicable.

go/apps/api/routes/v2_keys_add_permissions/handler.go (4)

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.

Learnt from: Flo4604
PR: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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.

Learnt from: chronark
PR: #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: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

go/apps/api/routes/v2_keys_set_permissions/handler.go (4)

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.

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.

Learnt from: chronark
PR: #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: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

go/apps/api/openapi/openapi-generated.yaml (6)

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.

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.

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: #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: #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: MichaelUnkey
PR: #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.

🧬 Code Graph Analysis (15)
go/apps/api/routes/v2_keys_add_permissions/401_test.go (2)
go/pkg/testutil/seed/seed.go (1)
  • CreateApiRequest (78-86)
go/pkg/uid/uid.go (1)
  • TestPrefix (24-24)
go/pkg/db/key_permission_insert.sql_generated.go (2)
internal/db/src/types.ts (1)
  • InsertKeyPermission (41-41)
go/pkg/hydra/store/db.go (1)
  • DBTX (8-13)
go/apps/api/routes/v2_permissions_get_permission/400_test.go (2)
go/pkg/rbac/query.go (1)
  • T (84-90)
go/apps/api/routes/v2_permissions_get_permission/handler.go (1)
  • Request (17-17)
go/apps/api/routes/v2_permissions_delete_permission/403_test.go (1)
go/apps/api/openapi/gen.go (1)
  • Permission (366-386)
go/apps/api/routes/v2_permissions_delete_permission/401_test.go (1)
go/apps/api/openapi/gen.go (1)
  • Permission (366-386)
go/apps/api/routes/v2_permissions_delete_permission/404_test.go (1)
go/apps/api/openapi/gen.go (1)
  • Permission (366-386)
go/apps/api/routes/v2_permissions_get_permission/handler.go (2)
go/pkg/db/permission_find_by_id_or_slug.sql_generated.go (1)
  • FindPermissionByIdOrSlugParams (18-21)
go/apps/api/openapi/gen.go (1)
  • Permission (366-386)
go/apps/api/routes/v2_keys_add_permissions/403_test.go (3)
go/pkg/testutil/seed/seed.go (1)
  • CreateApiRequest (78-86)
go/pkg/uid/uid.go (1)
  • TestPrefix (24-24)
go/apps/api/routes/v2_keys_add_permissions/handler.go (1)
  • Request (25-25)
go/pkg/db/permission_find_many_by_id_or_slug.sql_generated.go (2)
go/pkg/hydra/store/db.go (1)
  • DBTX (8-13)
go/apps/api/openapi/gen.go (1)
  • Permission (366-386)
go/pkg/zen/middleware_tracing.go (1)
go/pkg/otel/tracing/trace.go (1)
  • RecordError (79-84)
go/pkg/db/key_permission_delete_many_by_key_and_permission_ids.sql_generated.go (1)
go/pkg/hydra/store/db.go (1)
  • DBTX (8-13)
go/apps/api/routes/v2_permissions_get_permission/200_test.go (3)
go/pkg/uid/uid.go (1)
  • PermissionPrefix (27-27)
go/apps/api/openapi/gen.go (1)
  • Permission (366-386)
go/pkg/testutil/http.go (1)
  • CallRoute (257-291)
go/apps/api/routes/v2_keys_add_permissions/404_test.go (2)
go/pkg/uid/uid.go (1)
  • PermissionPrefix (27-27)
go/apps/api/routes/v2_keys_add_permissions/handler.go (1)
  • Request (25-25)
go/pkg/db/querier_generated.go (5)
go/pkg/hydra/store/db.go (1)
  • DBTX (8-13)
go/pkg/db/key_permission_delete_many_by_key_and_permission_ids.sql_generated.go (1)
  • DeleteManyKeyPermissionByKeyAndPermissionIDsParams (18-21)
go/pkg/db/permission_find_many_by_id_or_slug.sql_generated.go (1)
  • FindManyPermissionsByIdOrSlugParams (19-22)
go/pkg/db/models_generated.go (1)
  • Permission (744-752)
go/pkg/db/permission_find_by_id_or_slug.sql_generated.go (1)
  • FindPermissionByIdOrSlugParams (18-21)
go/apps/api/openapi/gen.go (2)
go/pkg/codes/unkey_data.go (1)
  • Data (87-128)
go/pkg/db/models_generated.go (1)
  • Permission (744-752)
⏰ 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). (6)
  • GitHub Check: Test Go API Local / Test
  • GitHub Check: Test Agent Local / test_agent_local
  • GitHub Check: Build / Build
  • GitHub Check: Test API / API Test Local
  • GitHub Check: Test Packages / Test
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (152)
go/apps/api/routes/v2_permissions_get_permission/403_test.go (2)

59-59: Field name update aligns with API standardization.

The change from PermissionId to Permission correctly reflects the unified permission identifier field that now accepts either ID or slug.


86-86: Consistent field name standardization.

Good consistency in applying the same field name change across both test cases.

go/apps/api/routes/v2_permissions_delete_role/handler.go (2)

125-125: Good use of audit log constants.

Replacing the hardcoded string with auditlog.RoleDeleteEvent improves consistency and maintainability across the codebase.


135-135: Consistent use of resource type constants.

Good consistency in applying the same constant-based approach for both event and resource type fields in the audit log.

go/apps/api/openapi/spec/common/KeyResponseData.yaml (1)

63-63: Improved API documentation clarity.

The updated description accurately specifies that the array contains permission slugs, providing clearer guidance for API consumers.

go/pkg/db/queries/permission_find_by_id_or_slug.sql (1)

1-4: Well-designed query for unified permission lookup.

The query correctly implements workspace-scoped permission lookup by either ID or slug, supporting the PR's objective to allow flexible permission identification.

go/apps/api/routes/v2_permissions_delete_permission/403_test.go (2)

60-60: Field name standardization applied correctly.

The change from PermissionId to Permission aligns with the unified permission identifier approach across the API.


95-95: Consistent field name update across test cases.

Good consistency in applying the same field name standardization across both authorization error test scenarios.

go/apps/api/routes/v2_permissions_get_permission/404_test.go (1)

41-41: Field rename aligns with API contract changes.

The field rename from PermissionId to Permission correctly reflects the API model change that allows endpoints to accept either permission ID or slug. The test logic remains intact and appropriate.

Also applies to: 62-62

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

209-209: Good practice: Using constants instead of magic strings.

Replacing hardcoded string literals with auditlog.KeyResourceType and auditlog.RoleResourceType constants improves maintainability and reduces the risk of typos in audit log entries.

Also applies to: 216-216

go/pkg/db/plugins/bulk-insert/template.go (1)

27-28: Well-designed enhancement for bulk insert operations.

The addition of ValuesFields and UpdateFields provides clear separation between fields used in the INSERT VALUES clause and the ON DUPLICATE KEY UPDATE clause. The descriptive comments make the purpose of each field clear.

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

228-228: Consistent audit log standardization.

Replacing hardcoded resource type strings with auditlog.KeyResourceType and auditlog.RoleResourceType constants in both role removal and addition audit logs maintains consistency across the codebase and improves maintainability.

Also applies to: 235-235, 272-272, 279-279

go/apps/api/openapi/spec/common/EmptyResponse.yaml (1)

4-4: Appropriate generalization for reusable schema.

Changing the description from operation-specific "successful key deletion" to generic "successful operation" makes this EmptyResponse schema more reusable across different endpoints while maintaining the same semantic meaning.

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

60-60: Field name change aligns with API refactor.

The change from PermissionId to Permission is consistent with the PR objective to allow either permission ID or slug in permission-related requests.

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

212-212: LGTM: Audit log constants improve consistency.

Replacing hardcoded strings with auditlog.KeyResourceType and auditlog.RoleResourceType constants improves maintainability and ensures consistent resource type identifiers across the codebase.

Also applies to: 219-219

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

109-109: LGTM: Audit log constants improve consistency.

Replacing hardcoded strings with auditlog.RoleCreateEvent and auditlog.RoleResourceType constants improves maintainability and ensures consistent event and resource type identifiers across the codebase.

Also applies to: 119-119

go/apps/api/routes/v2_permissions_get_permission/401_test.go (1)

26-26: Field name change aligns with API refactor.

The change from PermissionId to Permission is consistent with the broader API standardization to allow either permission ID or slug in permission-related requests.

go/pkg/zen/middleware_tracing.go (2)

7-7: LGTM: Import added for OpenTelemetry attributes.

The import is correctly added to support the new span attribute functionality.


25-25: LGTM: Request ID attribute enhances observability.

Adding the request ID as a span attribute improves traceability and correlation of requests across the system. The implementation correctly sets the attribute after span creation and before completion.

go/apps/api/routes/v2_permissions_delete_permission/401_test.go (1)

27-27: Field rename aligns with API standardization.

The change from PermissionId to Permission correctly reflects the API update that allows either permission ID or slug to be provided in permission-related requests.

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

166-166: Response type standardization approved.

The change to use openapi.EmptyResponse{} standardizes empty success responses across the API, which improves consistency and maintainability.

go/apps/api/routes/v2_permissions_delete_permission/200_test.go (1)

67-67: Consistent field rename across test cases.

Both test cases correctly use the updated Permission field name, maintaining consistency with the API model changes that allow either permission ID or slug.

Also applies to: 126-126

go/pkg/db/queries/key_permission_delete_many_by_key_and_permission_ids.sql (1)

1-3: Well-implemented batch delete query.

The SQL query correctly uses sqlc parameterization with sqlc.arg() and sqlc.slice() for safe batch deletion of key-permission associations. This approach improves performance by replacing multiple individual delete operations with a single batch operation.

go/apps/api/routes/v2_keys_remove_permissions/401_test.go (1)

59-60: Improved test structure with simplified request format.

The changes improve the test in two ways:

  1. Using helper functions (CreateApi, CreateKey) instead of manual database operations makes the test more maintainable
  2. Simplifying the Permissions field from complex objects to a simple string slice aligns with the API standardization
go/pkg/db/queries/key_permission_insert.sql (1)

12-12: LGTM! Well-implemented upsert pattern.

The addition of the ON DUPLICATE KEY UPDATE clause transforms this into an efficient upsert operation that handles both new insertions and updates to existing key-permission associations. This supports the batch operations mentioned in the PR objectives while maintaining proper timestamp tracking.

go/apps/api/routes/v2_permissions_delete_permission/404_test.go (3)

47-47: LGTM! Field rename aligns with API model changes.

The change from PermissionId to Permission correctly reflects the API model updates that allow either permission ID or slug. The test coverage remains comprehensive and tests important error scenarios.


68-68: Consistent field usage.

Good consistency in using the renamed Permission field across all test cases.


104-104: Proper test adaptation.

The field rename is correctly applied to the already-deleted permission test case, maintaining the test's intent while conforming to the new API contract.

go/apps/api/routes/v2_keys_set_permissions/401_test.go (1)

79-81: LGTM! API simplification improves usability.

The change from complex permission objects to a simple string array makes the API more intuitive while maintaining the same functionality. This aligns well with the PR objective to accept either permission ID or slug.

go/apps/api/openapi/spec/paths/v2/keys/addPermissions/V2KeysAddPermissionsResponseData.yaml (1)

15-15: LGTM! Good schema standardization.

Replacing the inline permission object definition with a reference to the shared Permission.yaml schema improves consistency and maintainability across the API while preserving the same semantic meaning.

go/pkg/db/queries/permission_find_many_by_id_or_slug.sql (1)

1-4: LGTM! Well-designed batch permission lookup query.

This query effectively supports the PR's core objective of accepting either permission IDs or slugs. The implementation correctly:

  • Uses workspace scoping for security isolation
  • Leverages sqlc.slice for safe parameterized batch queries
  • Handles both ID and slug matching in a single efficient query
go/apps/api/routes/v2_permissions_create_permission/handler.go (2)

99-99: Good standardization of audit log event constants.

Replacing the hardcoded string with auditlog.PermissionCreateEvent improves maintainability and consistency across the codebase.


109-111: Appropriate audit log resource type standardization and field correction.

The changes correctly:

  • Use auditlog.PermissionResourceType constant for consistency
  • Set the resource Name to req.Slug instead of req.Name, which is more appropriate since slug serves as the unique identifier for permissions
go/apps/api/openapi/spec/paths/v2/permissions/deleteRole/V2PermissionsDeleteRoleResponseBody.yaml (1)

4-9: Good API response standardization.

Adding the required data property with EmptyResponse reference creates consistency across delete endpoints and provides a standardized response structure throughout the API.

go/apps/api/routes/v2_keys_create_key/handler.go (4)

429-429: Good use of audit log resource type constants.

Replacing hardcoded strings with auditlog.KeyResourceType improves consistency and maintainability.


436-436: Consistent audit log standardization.

Using auditlog.PermissionResourceType constant aligns with the broader audit log standardization effort.


516-516: Proper audit log resource type constants for key and role.

Both auditlog.KeyResourceType and auditlog.RoleResourceType constants are correctly used, maintaining consistency with the standardization pattern.

Also applies to: 523-523


558-558: Complete audit log standardization for key creation.

The use of auditlog.KeyResourceType and auditlog.APIResourceType constants completes the standardization of audit log resource types in this handler.

Also applies to: 565-565

go/apps/api/openapi/spec/paths/v2/permissions/deletePermission/V2PermissionsDeletePermissionResponseBody.yaml (1)

4-9: Consistent API response structure standardization.

Adding the required data property with EmptyResponse reference maintains consistency with other delete endpoints and provides a unified response format across the API.

go/apps/api/openapi/spec/paths/v2/keys/deleteKey/V2KeysDeleteKeyResponseBody.yaml (1)

4-4: LGTM! Good standardization of empty response format.

The addition of data as a required property and the reference to the common EmptyResponse.yaml schema aligns well with the API standardization effort mentioned in the PR objectives.

Also applies to: 9-9

go/apps/api/routes/v2_keys_remove_permissions/200_test.go (2)

35-35: LGTM! Proper RBAC permission scope added.

Adding the "rbac.*.remove_permission_from_key" permission ensures the root key has the necessary authorization for the permission removal operations being tested.


78-79: Excellent API simplification!

The change from complex permission objects to simple string slices significantly improves the API's usability. The tests properly demonstrate that both permission IDs and names/slugs are accepted, which aligns perfectly with the PR objective of allowing "either permission id or slug in permission related requests."

Also applies to: 145-146, 222-223, 272-273, 361-362, 459-463

go/pkg/db/plugins/bulk-insert/parser.go (2)

14-17: LGTM! Clear addition for parameter separation.

The new ValuesPlaceholderCount field provides a clean way to track the number of placeholders in the VALUES clause, which is essential for properly separating VALUES parameters from UPDATE parameters in bulk insert operations.


124-133: Simple and effective placeholder counting implementation.

The countPlaceholders method is straightforward and correctly counts ? characters in the values clause. The implementation is efficient and follows Go conventions.

go/apps/api/openapi/spec/paths/v2/permissions/getPermission/V2PermissionsGetPermissionRequestBody.yaml (2)

3-3: LGTM! Good field name simplification.

Renaming from permissionId to permission makes the API more intuitive and aligns with the PR objective of accepting either permission ID or slug.

Also applies to: 5-5


9-9: Improved regex pattern for flexible permission identifiers.

The updated pattern "^[a-zA-Z][a-zA-Z0-9._-]*$" is more flexible than the previous one, allowing dots and hyphens while still enforcing that identifiers start with a letter. This supports both permission IDs and slugs effectively.

go/apps/api/openapi/spec/paths/v2/keys/setPermissions/V2KeysSetPermissionsResponseData.yaml (1)

15-15: Excellent schema standardization!

Replacing the inline permission object definition with a reference to the common Permission.yaml schema promotes consistency and reusability across the API. This aligns well with the broader API model simplification effort in this PR.

go/pkg/db/bulk_key_permission_insert.sql.go (2)

12-12: LGTM: SQL query correctly implements upsert functionality.

The addition of ON DUPLICATE KEY UPDATE updated_at_m = ? enables efficient upsert operations for key-permission associations.


38-41: LGTM: Parameter handling correctly aligned with SQL query.

The logic properly appends the UpdatedAt parameter once after all value parameters, matching the single ? placeholder in the ON DUPLICATE KEY UPDATE clause.

go/pkg/db/bulk_ratelimit_override_insert.sql.go (1)

43-48: LGTM: Consistent parameter handling pattern.

The argument collection logic correctly follows the same pattern as the key permissions file, appending UpdatedAt once after all value parameters to match the SQL query structure.

go/pkg/db/plugins/bulk-insert/bulk_insert.go.tmpl (2)

35-37: LGTM: Template correctly separates value and update fields.

The template now properly distinguishes between ValuesFields (for the VALUES clause) and UpdateFields (for ON DUPLICATE KEY UPDATE), appending only value fields per argument.


39-47: LGTM: Update fields handled correctly.

The conditional logic properly appends UpdateFields only once after all value arguments, which aligns with the SQL requirement for ON DUPLICATE KEY UPDATE parameters.

go/apps/api/routes/v2_permissions_get_permission/400_test.go (2)

37-51: LGTM: Test correctly updated for unified permission identifier.

The test name and request structure properly reflect the API change to accept either permission ID or slug in the Permission field.


53-67: LGTM: Consistent test pattern for empty permission field.

The test correctly validates the empty permission identifier case using the new Permission field name.

go/apps/api/routes/v2_permissions_delete_permission/handler.go (2)

64-81: LGTM: Unified permission lookup with proper error handling.

The handler correctly uses FindPermissionByIdOrSlug to support both permission IDs and slugs, with appropriate workspace scoping and error handling.


84-106: LGTM: Deletion operations use resolved permission ID.

The deletion operations correctly use permission.ID from the resolved permission record rather than the raw request value, ensuring consistency.

go/apps/api/routes/v2_keys_add_permissions/401_test.go (4)

15-15: LGTM: Import addition aligns with refactoring.

The addition of the testutil/seed import is appropriate for the refactored test setup using helper functions.


36-44: LGTM: Cleaner test setup with helper functions.

The refactoring from manual database inserts to the CreateApi helper function improves test maintainability and reduces boilerplate code. The helper ensures consistent API creation across tests.


46-53: LGTM: Consistent use of helper functions.

The CreateKey helper function usage is consistent with the API creation pattern and simplifies the test setup.


66-67: LGTM: Simplified permission structure aligns with API changes.

The change from a complex permission object structure to a simple string array aligns perfectly with the PR objective to allow either permission ID or slug in permission-related requests. This simplification makes the API more intuitive to use.

go/apps/api/openapi/spec/paths/v2/keys/updateKey/V2KeysUpdateKeyResponseBody.yaml (2)

4-4: LGTM: Standardizes response structure.

Making the data field required ensures consistency across API endpoints and aligns with the broader API standardization effort.


9-9: LGTM: Unified empty response schema.

The change to reference the common EmptyResponse.yaml schema reduces duplication and ensures consistent empty response structures across the API. This is a good architectural improvement.

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

63-66: LGTM: Enhanced flexibility with proper security scoping.

The change to FindPermissionByIdOrSlug successfully implements the PR objective of accepting either permission ID or slug. The workspace scoping is properly maintained through the query parameters, ensuring security while adding flexibility.

The unified req.Permission field name is more intuitive than the previous req.PermissionId.

go/apps/api/routes/v2_keys_set_permissions/400_test.go (2)

148-149: LGTM: Simplified permission structure in tests.

The change from complex permission objects to a simple string array aligns with the API simplification objectives. Testing with an empty string is appropriate for validating the new string-based permission format.


162-162: LGTM: Updated error assertion for new validation.

The change to expect generic "validate schema" errors is appropriate given the simplified permission structure and updated validation logic at the OpenAPI schema layer.

go/apps/api/routes/v2_keys_remove_permissions/404_test.go (2)

37-37: LGTM: Appropriate RBAC permission for remove operations.

The addition of "rbac.*.remove_permission_from_key" to the root key permissions is correct for testing the remove permissions endpoint. This ensures the test has the necessary authorization to perform the operation being tested.


59-60: LGTM: Consistent simplification across all test cases.

The change from complex permission objects to simple string arrays is consistently applied across all test cases. This aligns with the API simplification and maintains the same test coverage while using the new, more intuitive permission format.

Also applies to: 97-98, 168-169, 239-240

go/apps/api/routes/v2_keys_update_key/handler.go (5)

109-110: Good practice: Audit log initialization inside transaction.

Moving the audit log slice initialization inside the transaction ensures atomicity and prevents partial audit log creation if the transaction fails.


415-440: Excellent audit logging implementation for permission creation.

The audit logging for permission creation is comprehensive with:

  • Proper event type constants
  • Detailed resource metadata
  • Actor information including remote IP and user agent

Using auditlog.PermissionResourceType constant instead of string literals improves maintainability.


460-468: Correct timestamp handling for permission insertion.

Using the same timestamp for both CreatedAt and UpdatedAt during initial insertion is the correct approach. The sql.NullInt64 wrapper properly handles the nullable field.


559-572: Good refactoring: Using resource type constants.

Replacing string literals with auditlog.KeyResourceType and auditlog.APIResourceType constants improves code maintainability and reduces the risk of typos.


588-593: Good API standardization with EmptyResponse.

Using openapi.EmptyResponse instead of an ad-hoc empty struct improves API consistency and maintainability across all endpoints.

go/apps/api/routes/v2_keys_remove_permissions/403_test.go (2)

63-66: API simplification looks good.

The change from complex permission objects to simple string arrays containing permission IDs aligns with the broader API simplification effort and improves usability.


141-144: Consistent API simplification.

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

173-176: Consistent test update for API simplification.

go/apps/api/openapi/spec/common/permission.yaml (1)

23-28: Minor description formatting change.

The slug description was simplified to a single line. Based on the AI summary, the createdAt field was also removed from this schema, which aligns with the API simplification effort to remove unnecessary temporal data from responses.

go/pkg/db/key_permission_insert.sql_generated.go (3)

10-10: Good addition of ON DUPLICATE KEY UPDATE clause.

The addition of ON DUPLICATE KEY UPDATE updated_at_m = ? enables idempotent permission assignments where re-adding an existing permission updates its timestamp rather than failing.

Also applies to: 13-25


27-33: Proper struct updates for the new SQL behavior.

The addition of db tags and the new UpdatedAt field properly support the ON DUPLICATE KEY UPDATE functionality. Using sql.NullInt64 correctly handles nullable timestamp values.


48-55: Correct parameter handling for updated SQL.

The function correctly passes all parameters including the new UpdatedAt field to match the SQL statement's placeholders.

go/apps/api/openapi/spec/paths/v2/permissions/deletePermission/V2PermissionsDeletePermissionRequestBody.yaml (2)

3-3: LGTM! Field rename accurately reflects dual ID/slug support.

The change from permissionId to permission correctly communicates that the field accepts either a permission ID or slug.

Also applies to: 5-5


13-13: Clear documentation of dual ID/slug support.

The description effectively communicates that both permission IDs and slugs are accepted.

go/apps/api/openapi/spec/paths/v2/keys/removePermissions/V2KeysRemovePermissionsResponseData.yaml (2)

12-12: Excellent schema unification.

Replacing the inline permission object definition with a reference to the shared Permission.yaml schema improves consistency and maintainability across the API.


5-5: Good consolidation of documentation.

The rename from "Important notes" to "Notes" is more concise while maintaining the essential information.

go/apps/api/routes/v2_keys_add_permissions/400_test.go (2)

36-36: Good addition of required RBAC permission.

Adding "rbac.*.add_permission_to_key" to the root key permissions correctly reflects the enhanced permission checks for this operation.


187-189: Correct simplification of request structure.

The change from complex permission objects to a simple string slice aligns with the API schema simplification. The empty array test case is properly updated.

go/apps/api/openapi/spec/paths/v2/keys/removePermissions/V2KeysRemovePermissionsRequestBody.yaml (2)

22-22: Clear documentation of dual ID/slug support.

The updated description effectively communicates that both permission IDs and slugs are accepted.


26-30: Good schema simplification with proper validation.

The change from complex object structure to simple string type with validation constraints simplifies the API while maintaining proper input validation. The pattern and length constraints are appropriate for both permission IDs and slugs.

go/apps/api/routes/v2_keys_add_permissions/200_test.go (4)

32-32: Correct addition of required RBAC permission.

Adding "rbac.*.add_permission_to_key" ensures the root key has the necessary permissions for the enhanced permission checks.


74-76: Excellent simplification of request structure.

The change from complex permission objects to simple string arrays makes the API much cleaner while maintaining the same functionality.


229-240: Good improvement in response validation logic.

Using a helper function to check for permission presence without assuming order is more robust than direct array index comparisons. This correctly handles the fact that the response order may not be deterministic.


213-214: Comprehensive test coverage maintained.

The test correctly demonstrates mixed usage of permission IDs and slugs in a single request, which validates the core functionality of this PR.

go/apps/api/routes/v2_keys_set_permissions/200_test.go (4)

35-35: LGTM! Appropriate RBAC permissions added.

The addition of "rbac.*.remove_permission_from_key" and "rbac.*.add_permission_to_key" permissions to the root key creation is appropriate for permission management operations and aligns with the broader RBAC enhancements in this PR.


109-111: LGTM! Simplified request structure aligns with API changes.

The simplification from complex permission objects to a straightforward string array for Permissions makes the API more intuitive and flexible, allowing either permission IDs or slugs as intended by the PR objectives.


125-136: Excellent defensive programming for response validation.

The contains helper function properly handles flexible ordering in the response data, making the test more robust against implementation changes while still validating the correct permissions are returned.


372-428: Great test coverage for automatic permission creation.

This test case effectively validates the on-the-fly permission creation feature mentioned in the PR objectives, ensuring that non-existent permission slugs are automatically created and properly assigned to keys.

go/apps/api/routes/v2_keys_set_permissions/403_test.go (2)

36-61: LGTM! Improved test setup with helper functions.

The refactoring to use CreateApi and CreateKey helper functions from the seed package makes the test setup cleaner and more maintainable compared to manual database operations.


74-75: LGTM! Consistent with simplified API structure.

The simplified Permissions field using a string array aligns with the API changes and maintains consistency across the test suite.

go/pkg/db/permission_find_by_id_or_slug.sql_generated.go (2)

15-16: LGTM! Proper workspace scoping and flexible ID/slug lookup.

The SQL query correctly scopes permissions to the workspace (preventing cross-workspace access) and efficiently handles lookup by either ID or slug using OR logic, which aligns perfectly with the PR objectives.


28-41: LGTM! Clean generated query implementation.

The generated function properly handles parameter binding, query execution, and result scanning. The reuse of the Search parameter for both ID and slug matching is efficient and correct.

go/pkg/db/permission_find_many_by_id_or_slug.sql_generated.go (1)

33-48: Fix duplicate parameter processing causing query parameter mismatch.

The code duplicates the parameter processing logic, adding each ID from arg.Ids to queryParams four times total (twice in lines 33-40, twice again in lines 41-48). However, the SQL query expects each ID only twice (once for the id IN clause, once for the slug IN clause).

 	queryParams = append(queryParams, arg.WorkspaceID)
 	if len(arg.Ids) > 0 {
 		for _, v := range arg.Ids {
 			queryParams = append(queryParams, v)
 		}
 		query = strings.Replace(query, "/*SLICE:ids*/?", strings.Repeat(",?", len(arg.Ids))[1:], 1)
 	} else {
 		query = strings.Replace(query, "/*SLICE:ids*/?", "NULL", 1)
 	}
-	if len(arg.Ids) > 0 {
-		for _, v := range arg.Ids {
-			queryParams = append(queryParams, v)
-		}
-		query = strings.Replace(query, "/*SLICE:ids*/?", strings.Repeat(",?", len(arg.Ids))[1:], 1)
-	} else {
-		query = strings.Replace(query, "/*SLICE:ids*/?", "NULL", 1)
-	}
+	// Second replacement for slug IN clause
+	if len(arg.Ids) > 0 {
+		for _, v := range arg.Ids {
+			queryParams = append(queryParams, v)
+		}
+		query = strings.Replace(query, "/*SLICE:ids*/?", strings.Repeat(",?", len(arg.Ids))[1:], 1)
+	} else {
+		query = strings.Replace(query, "/*SLICE:ids*/?", "NULL", 1)
+	}

Likely an incorrect or invalid review comment.

go/pkg/db/key_permission_delete_many_by_key_and_permission_ids.sql_generated.go (2)

14-16: LGTM! Efficient batch deletion query design.

The SQL DELETE statement correctly targets the keys_permissions junction table with proper filtering by key_id and a list of permission_id values, enabling efficient batch removal of multiple permissions from a key in a single database operation.


31-38: LGTM! Proper dynamic query construction.

The slice replacement logic correctly handles both non-empty slices (building appropriate placeholder list) and empty slices (using NULL), ensuring the query remains valid in all cases while avoiding SQL injection vulnerabilities.

go/apps/api/routes/v2_permissions_get_permission/200_test.go (4)

42-56: LGTM: Good test setup refactoring.

Moving the permission creation outside individual test cases reduces duplication and makes the test more maintainable. The setup correctly creates all necessary fields including the slug.


59-84: LGTM: Correct field rename and test logic.

The field rename from PermissionId to Permission aligns with the PR objective of accepting either ID or slug. The test assertions properly verify all expected fields.


86-106: LGTM: Good test coverage for slug-based retrieval.

The new test case properly verifies that permissions can be retrieved by slug, which is a key feature of this PR. The assertions correctly verify that the same permission data is returned regardless of whether accessed by ID or slug.


126-126: LGTM: Consistent field usage.

The field name change is consistently applied across all test cases.

go/apps/api/routes/v2_keys_add_permissions/404_test.go (5)

36-36: LGTM: Correct permission scope addition.

Adding rbac.*.add_permission_to_key to the root key permissions is appropriate for the enhanced functionality being tested.


60-62: LGTM: Simplified request structure.

The simplification from complex permission objects to a string slice makes the API more intuitive and aligns with the PR objectives of accepting either ID or slug as strings.


95-95: LGTM: Correct permission prefix usage.

Using uid.PermissionPrefix instead of uid.TestPrefix is more semantically correct for permission ID generation in tests.


98-100: LGTM: Consistent simplified request structure.

The simplified request structure is consistently applied across test cases.


154-156: LGTM: Consistent field usage across test cases.

The simplified request structure is properly maintained across all test scenarios.

go/apps/api/openapi/spec/paths/v2/keys/addPermissions/V2KeysAddPermissionsRequestBody.yaml (3)

18-18: LGTM: Reasonable array size limit.

The increase to maxItems: 1000 provides good flexibility while maintaining reasonable bounds for bulk operations.


23-26: LGTM: Clear documentation of new behavior.

The documentation clearly explains that both permission slugs and IDs are accepted, and describes the auto-creation behavior when using slugs. This aligns perfectly with the PR objectives.


28-32: Pattern is intentional and consistent across key permission endpoints

The regex ^[a-zA-Z][a-zA-Z0-9._-]*$ is used uniformly by all V2 key-related request bodies (createKey, updateKey, addPermissions, etc.) to support hierarchical permission patterns (e.g. resource.resource_id.action) and wildcards. This differs from the slug pattern for permission entities (^[a-zA-Z0-9_]+$) and is documented in openapi-split.yaml (“Key permissions follow a hierarchical structure with patterns like apis.*.create_key”). You can safely ignore this suggestion.

Likely an incorrect or invalid review comment.

go/apps/api/routes/v2_keys_add_permissions/403_test.go (7)

15-15: LGTM: Appropriate import addition.

Adding the seed package import enables the use of helper functions for cleaner test setup.


36-44: LGTM: Improved test setup using helpers.

Replacing manual database inserts with helper functions makes the test more maintainable and consistent with other tests in the codebase.


46-61: LGTM: Consistent helper usage.

Using the CreateKey helper function provides better consistency across tests and reduces boilerplate code.


74-76: LGTM: Simplified request structure.

The simplified Permissions field as a string slice makes the API more intuitive and aligns with the PR objectives.


121-131: LGTM: Consistent test setup pattern.

The use of helper functions for creating workspace, API, and key entities follows a consistent pattern and improves test maintainability.

Also applies to: 133-148


151-151: LGTM: Correct permission scope.

Adding rbac.*.add_permission_to_key to the root key permissions is appropriate for testing the enhanced functionality.


154-156: LGTM: Consistent request structure.

The simplified request structure is consistently applied across all test scenarios.

go/pkg/db/plugins/bulk-insert/generator.go (3)

57-61: LGTM: More idiomatic prefix handling.

Using strings.CutPrefix is cleaner and more idiomatic than strings.TrimPrefix + strings.HasPrefix pattern. The boolean return value also provides better validation.


113-127: LGTM: Well-designed field separation logic.

The logic to separate fields based on ValuesPlaceholderCount is well-reasoned and correctly handles SQL queries with ON DUPLICATE KEY UPDATE clauses. The comment clearly explains the approach and the guarantee about parameter ordering from sqlc.


140-142: LGTM: Consistent template data structure.

Passing the separated ValuesFields and UpdateFields to the template renderer enables proper handling of complex SQL queries in the generated code.

go/apps/api/openapi/spec/paths/v2/keys/setPermissions/V2KeysSetPermissionsRequestBody.yaml (2)

6-14: Good security improvements for keyId validation.

The addition of maxLength and pattern constraints helps prevent injection attacks and ensures data integrity. The clarified description also helps distinguish between the database identifier and the actual API key string.


15-30: Breaking change: Simplified permissions format and regex update

The change replaces the previous object‐based permission definitions with a string array and updates the regex from ^[a-zA-Z0-9_]+$ to ^[a-zA-Z][a-zA-Z0-9._-]*$. This may break existing clients expecting objects or the old pattern.

Please verify manually:

  • That this breaking change is intentional and has been communicated to API consumers.
  • The new string‐only permissions format and regex are applied consistently across all OpenAPI specs under go/apps/api/openapi/spec/paths.
  • No remaining endpoints or test fixtures still reference the old object‐based structure or regex.
go/apps/api/routes/v2_keys_set_permissions/404_test.go (3)

35-35: LGTM! Root key permissions correctly updated.

The addition of rbac.*.remove_permission_from_key and rbac.*.add_permission_to_key permissions aligns with the new RBAC requirements in the handler.


95-95: Good fix: Corrected UID prefix for permission IDs.

Using uid.PermissionPrefix instead of uid.TestPrefix generates proper permission IDs with the "perm_" prefix, making the tests more realistic and accurate.

Also applies to: 195-195


58-61: Test requests correctly updated for new API format.

All test cases properly use string slices for permissions instead of the previous struct format, maintaining test coverage for the simplified API.

Also applies to: 97-100, 147-150, 197-200

go/pkg/db/querier_generated.go (1)

42-46: Generated database interfaces properly support new batch operations.

The new methods enable efficient batch deletions and lookups by ID or slug, while the ON DUPLICATE KEY UPDATE clause in InsertKeyPermission ensures idempotent permission assignments.

Also applies to: 312-317, 326-331, 746-746

go/apps/api/routes/v2_keys_add_permissions/handler.go (5)

58-65: Correct use of FindKeyByIdOrHash with explicit null handling.

The change properly uses the unified lookup method with clear null value handling for the hash parameter.


85-111: Excellent security enhancement with combined RBAC checks.

The requirement for both API update permissions and explicit rbac.*.add_permission_to_key permission provides proper separation of concerns and prevents unauthorized permission management.


187-289: Excellent transaction management and bulk operations.

The code properly uses transactions for consistency, performs efficient bulk inserts, and ensures audit logs are created atomically with the permission changes. Cache invalidation after the transaction is also correctly placed.


292-322: Clean response construction with proper nil handling.

The code correctly merges existing and new permissions, and properly handles the optional description field.


194-214: Good use of audit log constants.

Replacing hardcoded strings with auditlog package constants improves maintainability and prevents typos.

Also applies to: 252-264

go/apps/api/routes/v2_keys_remove_permissions/handler.go (4)

88-114: Correct RBAC implementation for remove operation.

The combined checks properly require both API update permissions and the specific rbac.*.remove_permission_from_key permission, maintaining security consistency with the add operation.


140-167: Robust permission validation and filtering.

The code properly validates all permissions exist before removal and filters to only remove permissions actually assigned to the key, making the operation idempotent and safe.


204-215: Efficient batch deletion implementation.

Using DeleteManyKeyPermissionByKeyAndPermissionIDs for batch deletion is much more efficient than individual delete operations.


230-244: Efficient response construction using in-memory state.

Excellent optimization - using the already-updated currentPermissionIDs map avoids an unnecessary database query while providing the correct remaining permissions.

go/apps/api/routes/v2_keys_set_permissions/handler.go (6)

61-67: Good improvement for key lookup flexibility.

The change from FindKeyByID to FindKeyByIdOrHash with explicit null handling allows for more flexible key lookups in the future while maintaining backward compatibility.


88-118: Enhanced security with combined RBAC checks.

The new permission structure properly enforces that setting permissions requires both update rights on the key AND explicit add/remove permission rights. This is a good security improvement that follows the principle of least privilege.


128-137: Efficient bulk permission lookup implementation.

The change to FindManyPermissionsByIdOrSlug improves performance by querying all permissions in a single database call and supports the PR objective of accepting either permission IDs or slugs.


139-183: Well-implemented permission resolution with auto-creation.

The logic correctly handles the PR requirement of accepting either permission ID or slug, and the auto-creation of non-system permissions (those not starting with "perm_") is implemented as specified. The dual deletion approach (lines 148-149) elegantly handles both ID and slug lookups.


215-296: Excellent use of batch operations for performance.

The implementation efficiently uses DeleteManyKeyPermissionByKeyAndPermissionIDs for batch deletion and InsertPermissions for batch creation, significantly improving performance compared to individual operations. The transaction handling ensures atomicity.


231-337: Comprehensive audit logging implementation.

The audit logging covers all operations (remove, create, add) with proper event types and detailed metadata. Good use of constants from the auditlog package for consistency.

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

1-4: Auto-generated code correctly regenerated from updated OpenAPI specs

The PR includes updates to the OpenAPI spec files under go/apps/api/openapi/spec/… to introduce EmptyResponse and support permission ID/slug fields, and the gen.go file reflects those changes via a fresh oapi-codegen run. No manual edits should be made to this generated file.

– Spec files updated (e.g. paths/v2/keys/addPermissions/V2KeysAddPermissionsRequestBody.yaml, paths/v2/apis/deleteApi/V2ApisDeleteApiResponseBody.yaml, etc.)
go/apps/api/openapi/gen.go regenerated by oapi-codegen v2.4.1

No further action required here; changes belong in the .yaml sources, not in gen.go.

go/apps/api/openapi/openapi-generated.yaml (6)

1-4: No concerns with generated header – safe to ignore.


204-214: 👍 Consistent empty-payload envelope
Requiring data and pointing it to #/components/schemas/EmptyResponse keeps responses uniform and aids SDK generation.


857-866: Uniform empty response envelope confirmed – LGTM


1312-1321: No issues – envelope consistent


1513-1527: Good – deletion accepts slug or ID and length 255 fits IDs


2115-2118: EmptyResponse addition approved – keeps SDK models DRY.

Copy link
Member Author

Flo4604 commented Jul 24, 2025

Copy link
Collaborator

@chronark chronark left a comment

Choose a reason for hiding this comment

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

let's think about how we can avoid relying on the prefix, the rest looks good

@github-actions
Copy link
Contributor

github-actions bot commented Jul 28, 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: 3

♻️ Duplicate comments (5)
go/apps/api/routes/v2_keys_set_permissions/403_test.go (1)

144-164: Note: Duplicate test case mentioned in past review

Based on previous review comments, there appears to be a duplicate of this test case later in the file. Please verify and remove any duplicate "permission for different resource" test cases to keep the test suite clean.

go/apps/api/openapi/openapi-generated.yaml (4)

568-574: Repeated regex/length block – factor out a PermissionIdentifier component.

This exact {minLength,maxLength,pattern} tuple now appears in add, remove, set and several other request bodies. Duplicating it invites drift. Define a reusable schema:

components:
  schemas:
    PermissionIdentifier:
      type: string
      minLength: 1
      maxLength: 100
      pattern: "^[a-zA-Z][a-zA-Z0-9._-]*$"

… and replace every inline items: block with

items:
  $ref: "#/components/schemas/PermissionIdentifier"

928-947: Same slug-only wording here – needs “slug or ID”.

See earlier comment for addPermissions. Keep the wording consistent across all permission arrays.


1030-1039: keyId pattern still too lax – does not enforce the required key_ prefix.

Docs (and uid.New(uid.KeyPrefix)) guarantee every DB key starts with key_. Leaving the pattern as ^[a-zA-Z0-9_]+$ accepts invalid IDs (fooBar). Tighten it:

-                    pattern: "^[a-zA-Z0-9_]+$"
+                    pattern: "^key_[a-zA-Z0-9]+$"

1602-1605: Description claims perm_ prefix is mandatory, but regex does not enforce it.

Exactly the same divergence as in the delete-permission body. Pick one approach – either require IDs here or accept both identifiers and adjust the description/pattern accordingly.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between a54bac8 and 8c76365.

📒 Files selected for processing (22)
  • go/apps/api/openapi/gen.go (13 hunks)
  • go/apps/api/openapi/openapi-generated.yaml (15 hunks)
  • go/apps/api/openapi/spec/common/permission.yaml (1 hunks)
  • go/apps/api/openapi/spec/paths/v2/keys/addPermissions/V2KeysAddPermissionsRequestBody.yaml (1 hunks)
  • go/apps/api/openapi/spec/paths/v2/keys/removePermissions/V2KeysRemovePermissionsRequestBody.yaml (1 hunks)
  • go/apps/api/openapi/spec/paths/v2/keys/setPermissions/V2KeysSetPermissionsRequestBody.yaml (1 hunks)
  • go/apps/api/openapi/spec/paths/v2/keys/updateKey/V2KeysUpdateKeyRequestBody.yaml (1 hunks)
  • go/apps/api/openapi/spec/paths/v2/keys/verifyKey/V2KeysVerifyKeyRequestBody.yaml (1 hunks)
  • go/apps/api/routes/v2_keys_add_permissions/200_test.go (7 hunks)
  • go/apps/api/routes/v2_keys_add_permissions/404_test.go (4 hunks)
  • go/apps/api/routes/v2_keys_add_permissions/handler.go (6 hunks)
  • go/apps/api/routes/v2_keys_create_key/handler.go (5 hunks)
  • go/apps/api/routes/v2_keys_remove_permissions/200_test.go (9 hunks)
  • go/apps/api/routes/v2_keys_remove_permissions/handler.go (5 hunks)
  • go/apps/api/routes/v2_keys_set_permissions/200_test.go (9 hunks)
  • go/apps/api/routes/v2_keys_set_permissions/403_test.go (3 hunks)
  • go/apps/api/routes/v2_keys_set_permissions/404_test.go (3 hunks)
  • go/apps/api/routes/v2_keys_set_permissions/handler.go (5 hunks)
  • go/apps/api/routes/v2_keys_update_key/handler.go (7 hunks)
  • go/pkg/db/permission_find_by_slugs.sql_generated.go (2 hunks)
  • go/pkg/db/querier_generated.go (4 hunks)
  • go/pkg/db/queries/permission_find_by_slugs.sql (1 hunks)
🧰 Additional context used
🧠 Learnings (22)
📓 Common learnings
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/apps/api/openapi/spec/paths/v2/keys/verifyKey/V2KeysVerifyKeyRequestBody.yaml (3)

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: 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.

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.

go/apps/api/routes/v2_keys_create_key/handler.go (4)

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.

Learnt from: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

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.

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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.

go/apps/api/routes/v2_keys_set_permissions/403_test.go (7)

Learnt from: MichaelUnkey
PR: #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: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: CR
PR: unkeyed/unkey#0
File: go/deploy/CLAUDE.md:0-0
Timestamp: 2025-07-21T18:05:58.236Z
Learning: Applies to go/deploy/**/*.{go,js,ts,tsx,py,sh,md,txt,json,yaml,yml,ini,env,conf,html,css,scss,xml,c,h,cpp,java,rb,rs,php,pl,sql} : 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.

Learnt from: CR
PR: unkeyed/unkey#0
File: go/deploy/CLAUDE.md:0-0
Timestamp: 2025-07-21T18:05:58.236Z
Learning: Applies to go/deploy/**/*.{go,js,ts,tsx,py,sh,md,txt,json,yaml,yml,ini,env,conf,html,css,scss,xml,c,h,cpp,java,rb,rs,php,pl,sql} : Do not remove AIDEV-*s without explicit human instruction.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use ctx.workspace.id directly instead of fetching the workspace separately for better performance and security.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: When querying or updating namespaces in the Unkey dashboard, always scope the operations to the current workspace using eq(table.workspaceId, ctx.workspace.id) to prevent cross-workspace access.

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.

go/apps/api/routes/v2_keys_add_permissions/200_test.go (4)

Learnt from: MichaelUnkey
PR: #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: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: #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: #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/db/permission_find_by_slugs.sql_generated.go (2)

Learnt from: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

Learnt from: chronark
PR: #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_add_permissions/404_test.go (4)

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: When querying or updating namespaces in the Unkey dashboard, always scope the operations to the current workspace using eq(table.workspaceId, ctx.workspace.id) to prevent cross-workspace access.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use ctx.workspace.id directly instead of fetching the workspace separately for better performance and security.

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.

go/apps/api/openapi/spec/paths/v2/keys/updateKey/V2KeysUpdateKeyRequestBody.yaml (6)

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.

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.

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: 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.

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.

Learnt from: MichaelUnkey
PR: #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.

go/apps/api/openapi/spec/paths/v2/keys/removePermissions/V2KeysRemovePermissionsRequestBody.yaml (5)

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: #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: #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: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

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.

go/apps/api/openapi/spec/common/permission.yaml (3)

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: #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: #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/apps/api/routes/v2_keys_remove_permissions/200_test.go (6)

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

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.

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.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use ctx.workspace.id directly instead of fetching the workspace separately for better performance and security.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: When querying or updating namespaces in the Unkey dashboard, always scope the operations to the current workspace using eq(table.workspaceId, ctx.workspace.id) to prevent cross-workspace access.

go/apps/api/routes/v2_keys_set_permissions/200_test.go (6)

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.

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: #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: MichaelUnkey
PR: #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: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: When querying or updating namespaces in the Unkey dashboard, always scope the operations to the current workspace using eq(table.workspaceId, ctx.workspace.id) to prevent cross-workspace access.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use ctx.workspace.id directly instead of fetching the workspace separately for better performance and security.

go/apps/api/openapi/spec/paths/v2/keys/addPermissions/V2KeysAddPermissionsRequestBody.yaml (6)

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: #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: #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: #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.

Learnt from: chronark
PR: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

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.

go/apps/api/routes/v2_keys_set_permissions/404_test.go (8)

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: MichaelUnkey
PR: #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: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

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.

Learnt from: chronark
PR: #2126
File: apps/api/src/routes/v1_ratelimit_getOverride.happy.test.ts:36-36
Timestamp: 2024-11-13T19:06:36.786Z
Learning: In the rate limit test files (e.g., apps/api/src/routes/v1_ratelimit_getOverride.happy.test.ts), URL parameters like namespaceId and identifier do not need to be URL-encoded in the test code because the values used are always considered safe within the test environment.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: When querying or updating namespaces in the Unkey dashboard, always scope the operations to the current workspace using eq(table.workspaceId, ctx.workspace.id) to prevent cross-workspace access.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use ctx.workspace.id directly instead of fetching the workspace separately for better performance and security.

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.

go/apps/api/openapi/spec/paths/v2/keys/setPermissions/V2KeysSetPermissionsRequestBody.yaml (6)

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: #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: #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: #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.

Learnt from: chronark
PR: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

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.

go/apps/api/routes/v2_keys_update_key/handler.go (6)

Learnt from: chronark
PR: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: #2146
File: apps/dashboard/lib/trpc/routers/api/setDefaultPrefix.ts:80-80
Timestamp: 2024-10-04T17:27:08.666Z
Learning: Ensure that audit log descriptions accurately reflect the action being performed, such as updating the defaultPrefix, and avoid incorrect references like 'name' when not applicable.

Learnt from: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

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.

Learnt from: Flo4604
PR: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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/apps/api/routes/v2_keys_remove_permissions/handler.go (8)

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.

Learnt from: chronark
PR: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: #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: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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.

Learnt from: chronark
PR: #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: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use ctx.workspace.id directly instead of fetching the workspace separately for better performance and security.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: When querying or updating namespaces in the Unkey dashboard, always scope the operations to the current workspace using eq(table.workspaceId, ctx.workspace.id) to prevent cross-workspace access.

go/apps/api/routes/v2_keys_add_permissions/handler.go (6)

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.

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.

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: Flo4604
PR: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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.

Learnt from: chronark
PR: #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: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

go/apps/api/routes/v2_keys_set_permissions/handler.go (5)

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.

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.

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: #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: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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/db/querier_generated.go (1)

Learnt from: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

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

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.

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: CR
PR: unkeyed/unkey#0
File: go/deploy/CLAUDE.md:0-0
Timestamp: 2025-07-21T18:05:58.236Z
Learning: Applies to go/deploy/**/*.{go,js,ts,tsx,py,sh,md,txt,json,yaml,yml,ini,env,conf,html,css,scss,xml,c,h,cpp,java,rb,rs,php,pl,sql} : Do not remove AIDEV-*s without explicit human instruction.

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.

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

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.

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.

Learnt from: Flo4604
PR: #3647
File: go/apps/api/openapi/openapi-generated.yaml:3569-3575
Timestamp: 2025-07-22T18:09:41.800Z
Learning: In the Unkey codebase, using non-standard HTTP status code 529 for internal-only endpoints is acceptable and should not be flagged as an issue in future reviews.

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.

Learnt from: CR
PR: unkeyed/unkey#0
File: go/deploy/CLAUDE.md:0-0
Timestamp: 2025-07-21T18:05:58.236Z
Learning: Applies to go/deploy/**/*.{env,sh,yaml,yml,json,conf,ini} : All environment variables MUST follow the format UNKEY_<SERVICE_NAME>_VARNAME.

Learnt from: CR
PR: unkeyed/unkey#0
File: go/deploy/CLAUDE.md:0-0
Timestamp: 2025-07-21T18:05:58.236Z
Learning: Applies to go/deploy/**/*.{go,js,ts,tsx,py,sh,md,txt,json,yaml,yml,ini,env,conf,html,css,scss,xml,c,h,cpp,java,rb,rs,php,pl,sql} : 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.

go/apps/api/openapi/openapi-generated.yaml (14)

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.

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.

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: #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: #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: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

Learnt from: CR
PR: unkeyed/unkey#0
File: go/deploy/CLAUDE.md:0-0
Timestamp: 2025-07-21T18:05:58.236Z
Learning: Applies to go/deploy/**/*.{go,js,ts,tsx,py,sh,md,txt,json,yaml,yml,ini,env,conf,html,css,scss,xml,c,h,cpp,java,rb,rs,php,pl,sql} : Do not remove AIDEV-*s without explicit human instruction.

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

Learnt from: CR
PR: unkeyed/unkey#0
File: go/deploy/CLAUDE.md:0-0
Timestamp: 2025-07-21T18:05:58.236Z
Learning: Applies to go/deploy/**/*.{go,js,ts,tsx,py,sh,md,txt,json,yaml,yml,ini,env,conf,html,css,scss,xml,c,h,cpp,java,rb,rs,php,pl,sql} : 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.

Learnt from: MichaelUnkey
PR: #3173
File: apps/docs/security/delete-protection.mdx:32-36
Timestamp: 2025-04-22T17:33:28.162Z
Learning: In the Unkey dashboard UI for delete protection, the button/link to initiate the process is labeled "Disable Delete Protection" while the confirmation button is labeled "Disable API Delete Protection". The documentation should maintain these different labels to match the actual UI.

Learnt from: MichaelUnkey
PR: #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: #2146
File: apps/dashboard/lib/trpc/routers/api/setDefaultPrefix.ts:80-80
Timestamp: 2024-10-04T17:27:08.666Z
Learning: Ensure that audit log descriptions accurately reflect the action being performed, such as updating the defaultPrefix, and avoid incorrect references like 'name' when not applicable.

Learnt from: MichaelUnkey
PR: #3173
File: apps/docs/security/delete-protection.mdx:21-24
Timestamp: 2025-04-22T17:34:04.438Z
Learning: In the Unkey dashboard UI for enabling delete protection, the button/link to initiate the process is labeled "Enable Delete Protection" while the confirmation button is labeled "Enable API Delete Protection". The documentation should maintain these different labels to match the actual UI.

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

🧬 Code Graph Analysis (7)
go/apps/api/routes/v2_keys_set_permissions/403_test.go (2)
go/pkg/testutil/seed/seed.go (1)
  • CreateApiRequest (78-86)
go/pkg/uid/uid.go (1)
  • PermissionPrefix (27-27)
go/apps/api/routes/v2_keys_add_permissions/200_test.go (4)
go/pkg/codes/unkey_data.go (1)
  • Data (87-128)
go/pkg/rbac/permissions.go (1)
  • CreatePermission (105-105)
go/pkg/testutil/seed/seed.go (1)
  • CreatePermissionRequest (396-401)
go/apps/api/routes/v2_keys_add_permissions/handler.go (1)
  • Request (24-24)
go/pkg/db/permission_find_by_slugs.sql_generated.go (2)
go/pkg/hydra/store/db.go (1)
  • DBTX (8-13)
go/apps/api/openapi/gen.go (1)
  • Permission (366-386)
go/apps/api/routes/v2_keys_add_permissions/404_test.go (1)
go/pkg/uid/uid.go (1)
  • PermissionPrefix (27-27)
go/apps/api/routes/v2_keys_remove_permissions/200_test.go (2)
go/pkg/testutil/seed/seed.go (1)
  • CreatePermissionRequest (396-401)
go/apps/api/routes/v2_keys_remove_permissions/handler.go (1)
  • Request (22-22)
go/pkg/db/querier_generated.go (5)
go/pkg/hydra/store/db.go (1)
  • DBTX (8-13)
go/pkg/db/key_permission_delete_many_by_key_and_permission_ids.sql_generated.go (1)
  • DeleteManyKeyPermissionByKeyAndPermissionIDsParams (18-21)
go/pkg/db/permission_find_by_id_or_slug.sql_generated.go (1)
  • FindPermissionByIdOrSlugParams (18-21)
go/pkg/db/models_generated.go (1)
  • Permission (744-752)
go/pkg/db/permission_find_by_slugs.sql_generated.go (1)
  • FindPermissionsBySlugsParams (17-20)
go/apps/api/openapi/gen.go (2)
go/pkg/codes/unkey_data.go (1)
  • Data (87-128)
go/pkg/db/models_generated.go (1)
  • Permission (744-752)
⏰ 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). (5)
  • GitHub Check: Test Agent Local / test_agent_local
  • GitHub Check: Build / Build
  • GitHub Check: Test Go API Local / Test
  • GitHub Check: Test API / API Test Local
  • GitHub Check: Test Packages / Test
🔇 Additional comments (42)
go/apps/api/openapi/spec/paths/v2/keys/verifyKey/V2KeysVerifyKeyRequestBody.yaml (1)

11-11: LGTM: Whitespace cleanup

Good housekeeping - removing trailing whitespace from the summary field.

go/pkg/db/queries/permission_find_by_slugs.sql (1)

2-2: LGTM: Enhanced query to return full permission data

Changing from SELECT id, slug to SELECT * provides complete permission objects, which aligns with the broader API refactoring to use full permission structs instead of partial data.

go/apps/api/routes/v2_keys_set_permissions/403_test.go (4)

15-15: LGTM: Improved test setup with helper functions

Good refactoring to use the seed package helpers instead of manual database insertions. This makes the test more maintainable and consistent with other tests.


36-61: LGTM: Clean test data creation

Using CreateApi and CreateKey helpers significantly simplifies the test setup and removes the need for manual timestamp and hash management.


63-63: LGTM: Correct permission ID prefix

Good fix - using uid.PermissionPrefix instead of the incorrect uid.TestPrefix ensures proper permission ID generation.


74-76: LGTM: Simplified request structure

The change from complex permission objects to a simple string slice aligns with the API simplification effort and makes the test more readable.

go/apps/api/openapi/spec/paths/v2/keys/updateKey/V2KeysUpdateKeyRequestBody.yaml (1)

121-123: LGTM: Enhanced permission validation

The validation updates are well-designed:

  • Increased minLength to 3 enforces more meaningful permission names
  • Updated regex pattern ^[a-zA-Z0-9_:\-\.\*]+$ appropriately supports:
    • Colons for namespaced permissions (e.g., api:read)
    • Asterisks for wildcard permissions (e.g., documents.*)
    • Removes the restrictive requirement for first character to be a letter

These changes align with the broader API simplification and provide more flexibility for permission naming conventions.

go/apps/api/routes/v2_keys_create_key/handler.go (4)

366-369: LGTM: Enhanced permission data handling

Good change from partial FindPermissionsBySlugsRow to full db.Permission structs. This provides complete permission information and aligns with the API's move toward using full permission objects.


372-372: LGTM: Consistent use of full Permission structs

Using complete db.Permission structs throughout the permission handling logic ensures consistency and provides all necessary permission data for downstream operations.

Also applies to: 391-399


387-387: LGTM: Reasonable default for auto-created permissions

Setting an empty description for auto-created permissions is appropriate - it can be updated later through the management API, and the slug/name fields provide sufficient identification.


434-434: LGTM: Audit log constants improve maintainability

Excellent refactoring to use auditlog package constants instead of string literals. This prevents typos, ensures consistency across the codebase, and makes audit log resource types centrally managed.

Also applies to: 441-441, 521-521, 528-528, 563-563, 570-570

go/apps/api/routes/v2_keys_update_key/handler.go (3)

383-412: LGTM! Improved permission handling with full objects.

The refactoring to use a map of full Permission objects and track requested permissions enables efficient lookups and proper metadata handling for auto-created permissions.


415-439: Excellent audit logging for permission creation.

The comprehensive audit logs capture all relevant details about auto-created permissions, improving traceability and compliance.


460-467: LGTM! Consistent timestamp handling.

Using the same timestamp for both CreatedAt and UpdatedAt when inserting new records is the correct approach.

go/apps/api/routes/v2_keys_remove_permissions/200_test.go (2)

35-35: LGTM! Proper RBAC permission for removing permissions.

The root key correctly includes the rbac.*.remove_permission_from_key permission required for this operation.


155-167: Good practice: Order-independent permission verification.

The contains helper function properly verifies permission presence without assuming a specific order in the response.

go/pkg/db/permission_find_by_slugs.sql_generated.go (1)

14-65: LGTM! Enhanced query returns complete permission data.

The updated query returns full Permission objects instead of partial data, enabling handlers to access all permission metadata for authorization checks and audit logging. This aligns with the API's simplified permission management approach.

go/apps/api/openapi/spec/common/permission.yaml (1)

24-24: Review permission slug regex for expanded character set

Please verify that the updated slug pattern in go/apps/api/openapi/spec/common/permission.yaml aligns with our validation requirements:

  • Location: Line 24
  • Current pattern:
    pattern: ^[a-zA-Z0-9_:\-\.\*]+$
  • Concern: This now permits :, -, ., and * in addition to alphanumeric and underscore, whereas existing slugs (per OpenAPI schema and handler validation) use only [a-zA-Z0-9_]+.
  • Action:
    • Confirm whether colons, hyphens, periods, and asterisks are intentionally allowed for permission slugs.
    • Ensure consistency with other slug validations across your OpenAPI specs and handlers, or revert the regex to enforce only alphanumeric and underscore if not intended.
go/apps/api/routes/v2_keys_add_permissions/200_test.go (1)

32-32: LGTM! Complete RBAC permissions for permission management.

The root key correctly includes both rbac.*.add_permission_to_key and rbac.*.create_permission permissions, enabling full permission management functionality including auto-creation.

go/apps/api/openapi/spec/paths/v2/keys/removePermissions/V2KeysRemovePermissionsRequestBody.yaml (1)

21-30: LGTM! Schema simplification improves API usability.

The change from complex permission objects to simple strings significantly simplifies the API while maintaining functionality. The expanded regex pattern ^[a-zA-Z0-9_:\-\.\*]+$ appropriately supports hierarchical permission naming conventions with separators like colons and dots.

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

112-114: LGTM! Simplified request structure improves usability.

The change from complex permission objects to a simple string array significantly simplifies the API while maintaining all necessary functionality.


128-140: Well-implemented response validation helper.

The contains helper function provides a clean way to verify response data without relying on specific ordering, which is more robust than fixed index assertions.


35-35: Please confirm the necessity of the expanded RBAC scopes

I couldn’t find any references in go/apps/api/routes/v2_keys_set_permissions/handler.go that check for or invoke a create_permission scope (or other new RBAC permissions). Before approving these additions, please:

  • Manually verify that the handler enforces or needs each of the newly added RBAC scopes (especially "rbac.*.create_permission").
  • If any of the expanded permissions aren’t actually used by the endpoint, consider removing them from the test setup to avoid confusion.
go/apps/api/routes/v2_keys_add_permissions/404_test.go (3)

36-36: LGTM! Appropriate RBAC permissions for add operation.

The root key correctly includes the "rbac.*.add_permission_to_key" permission scope needed for the add permissions functionality.


46-46: Correct permission ID prefix usage.

Good use of uid.PermissionPrefix instead of generic test prefix, aligning with the established UID conventions in the codebase.


117-119: Confirm Permission Resolution for IDs and Slugs

I wasn’t able to locate any explicit lookup or resolution logic in
go/apps/api/routes/v2_keys_add_permissions/handler.go that handles both raw UUIDs and human-friendly slugs for permissions. Please manually verify that:

  • The handler (or underlying service) accepts a mixed array of permission identifiers (both IDs and slugs).
  • Any repository or service call invoked by this handler normalizes or resolves slugs to their corresponding IDs before granting permissions.
go/apps/api/openapi/spec/paths/v2/keys/addPermissions/V2KeysAddPermissionsRequestBody.yaml (2)

25-30: LGTM! Well-designed schema with clear auto-creation behavior.

The simplified string-based permission format significantly improves API usability. The expanded regex pattern ^[a-zA-Z0-9_:\-\.\*]+$ appropriately supports hierarchical permission naming with separators, and the auto-creation behavior is clearly documented.


18-18: Appropriate limit for permission sets.

The maxItems: 1000 limit provides reasonable protection against abuse while allowing for complex applications with extensive permission sets.

go/apps/api/openapi/spec/paths/v2/keys/setPermissions/V2KeysSetPermissionsRequestBody.yaml (2)

9-14: LGTM! Consistent keyId validation.

The keyId validation constraints (maxLength: 255, pattern for alphanumeric + underscore) are appropriate and consistent with other permission endpoints.


18-27: Clear documentation of replacement behavior.

The description clearly explains that this is a "complete replacement operation" that overwrites existing direct permissions, which is important for API consumers to understand.

go/apps/api/routes/v2_keys_set_permissions/404_test.go (1)

35-35: LGTM! Test updates align with API simplifications.

The changes correctly reflect the simplified permission request format where permissions are now represented as strings (IDs or slugs) rather than complex objects. The additional RBAC permissions for the root key (rbac.*.add_permission_to_key and rbac.*.remove_permission_from_key) are appropriate for the permission management operations.

Also applies to: 59-60, 109-110

go/pkg/db/querier_generated.go (1)

42-46: Well-aligned database interface enhancements.

The generated database methods properly support the refactored permission management:

  • DeleteManyKeyPermissionByKeyAndPermissionIDs enables efficient batch deletion
  • FindPermissionByIdOrSlug supports unified permission lookups
  • FindPermissionsBySlugs now returns full Permission structs for richer responses
  • InsertKeyPermission includes ON DUPLICATE KEY UPDATE to handle idempotent operations

These changes align perfectly with the handler refactoring for improved efficiency and consistency.

Also applies to: 320-325, 342-345, 740-741

go/apps/api/routes/v2_keys_add_permissions/handler.go (2)

57-65: Excellent refactoring with improved efficiency and security.

The handler improvements are well-implemented:

  • RBAC checks properly combine update key and add permission requirements
  • Batch permission resolution via FindPermissionsBySlugs reduces database queries
  • Auto-creation of missing permissions is controlled by explicit RBAC checks
  • The removal of string prefix inference (no more checking for "perm_" prefix) addresses the previous concern about inferring behavior from strings

The batch processing approach significantly improves performance.

Also applies to: 84-110, 119-188


190-288: Well-structured transaction with comprehensive audit logging.

The transaction properly handles:

  • Batch insertion of new permissions with appropriate audit events
  • Batch assignment of permissions to keys using ON DUPLICATE KEY UPDATE
  • Standardized audit log constants from the auditlog package
  • Clear separation of permission creation vs assignment events

The audit trail will be invaluable for compliance and debugging.

go/apps/api/routes/v2_keys_set_permissions/handler.go (2)

87-118: Appropriate RBAC requirements for set operation.

The combined RBAC check correctly requires both AddPermissionToKey and RemovePermissionFromKey permissions, which is appropriate since the set operation can both add and remove permissions. This ensures proper authorization for the full scope of the operation.


188-214: Efficient permission diffing and batch operations.

The implementation efficiently determines which permissions to add and remove:

  • Clean mapping structures for O(1) lookups
  • Single batch delete operation for all removals
  • Proper audit logging for each removed permission
  • No unnecessary database queries

The approach minimizes database operations while maintaining data consistency.

Also applies to: 218-263

go/apps/api/routes/v2_keys_remove_permissions/handler.go (2)

88-114: Consistent implementation with proper validation.

The handler follows the established pattern:

  • Appropriate RBAC check for update key + remove permission
  • Batch permission lookup for efficiency
  • Proper validation that all requested permissions exist before removal
  • Clear error messages for missing permissions

The consistency across all permission handlers improves maintainability.

Also applies to: 129-156


169-228: Clean batch deletion with comprehensive audit trail.

The removal logic is well-implemented:

  • Efficient batch deletion via DeleteManyKeyPermissionByKeyAndPermissionIDs
  • Proper audit logging for each removed permission
  • Cache invalidation only when changes are made
  • Response built from the updated in-memory state

The implementation minimizes database operations while maintaining a complete audit trail.

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

1-2386: This is auto-generated code - do not modify directly

This file is generated from OpenAPI specifications as indicated by the header comment. Any changes should be made to the source OpenAPI specification files (in go/apps/api/openapi/spec/) and then regenerated using the code generation process.

The generated changes correctly reflect the PR objectives:

  • New EmptyResponse type for standardized empty responses
  • Simplified permission fields accepting []string for IDs or slugs
  • Updated documentation mentioning auto-creation behavior
go/apps/api/openapi/openapi-generated.yaml (3)

204-213: 👍 Good move replacing per-endpoint empty objects with a shared EmptyResponse.

The meta + data(EmptyResponse) envelope is consistent with the rest of the spec and avoids dozens of bespoke “{}” schemas.


2119-2123: LGTM – concise EmptyResponse component is clean and future-proof.


1523-1531: Pattern allows dots/- but IDs begin with perm_ – mismatch with “ID or slug” promise.

If a client passes an ID (perm_abcd), it matches; fine.
If the doc says “slug or ID”, the pattern should remain broad but the description (line 1527) must reflect that and drop the “Must begin with perm_” phrase. Either:

  1. Keep broad pattern and change description to “permission ID or slug”, or
  2. Split into two oneOf schemas to validate strictly.

Current mixed message will confuse integrators.

⛔ Skipped due to learnings
Learnt from: chronark
PR: unkeyed/unkey#3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: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: MichaelUnkey
PR: unkeyed/unkey#3173
File: apps/docs/security/delete-protection.mdx:32-36
Timestamp: 2025-04-22T17:33:28.162Z
Learning: In the Unkey dashboard UI for delete protection, the button/link to initiate the process is labeled "Disable Delete Protection" while the confirmation button is labeled "Disable API Delete Protection". The documentation should maintain these different labels to match the actual UI.
Learnt from: MichaelUnkey
PR: unkeyed/unkey#3173
File: apps/docs/security/delete-protection.mdx:21-24
Timestamp: 2025-04-22T17:34:04.438Z
Learning: In the Unkey dashboard UI for enabling delete protection, the button/link to initiate the process is labeled "Enable Delete Protection" while the confirmation button is labeled "Enable API Delete Protection". The documentation should maintain these different labels to match the actual UI.
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#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.

@Flo4604 Flo4604 requested a review from chronark July 28, 2025 10:10
@graphite-app
Copy link

graphite-app bot commented Jul 28, 2025

Photo gif. A hand giving a thumbs-up appears in front of a photo of Harrison Ford. (Added via Giphy)

feat: replace roleId with singular role field
@Flo4604 Flo4604 enabled auto-merge July 28, 2025 11:39
@graphite-app
Copy link

graphite-app bot commented Jul 28, 2025

Merge activity

  • Jul 28, 12:15 PM UTC: This pull request can not be added to the Graphite merge queue. Please try rebasing and resubmitting to merge when ready.
  • Jul 28, 12:15 PM UTC: Graphite disabled "merge when ready" on this PR due to: a merge conflict with the target branch; resolve the conflict and try again..

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: 16

🔭 Outside diff range comments (1)
go/apps/api/openapi/spec/paths/v2/permissions/deleteRole/V2PermissionsDeleteRoleRequestBody.yaml (1)

5-24: LGTM: Field renamed to support ID or name with appropriate validation.

The change from roleId to role with the relaxed pattern correctly implements the PR objective to accept either role ID or name. The validation pattern matches the established role name format from the codebase learnings.

Consider updating the example to show a role name instead of a role ID, or provide two examples to clarify both accepted formats:

-    example: role_dns_manager
+    examples:
+      role_id: role_dns_manager
+      role_name: dns_manager
♻️ Duplicate comments (10)
go/apps/api/routes/v2_keys_add_permissions/400_test.go (1)

202-202: Ensure Specific Validation Error Tests Are Retained

The test now only asserts a generic "failed to validate schema" error message, which reduces clarity on user-facing error feedback. This risks losing specificity about what exactly failed validation (e.g., missing permission ID or slug).

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

92-92: TODO already tracked for separate PR.

This TODO comment about role permission checks has already been discussed and will be addressed in a separate PR as confirmed by the author.

go/apps/api/routes/v2_keys_add_permissions/handler.go (2)

58-64: Use FindKeyByID instead of FindKeyByIdOrHash

Same as in the set permissions handler - since the hash parameter is always invalid, use the simpler FindKeyByID function.

-key, err := db.Query.FindKeyByIdOrHash(ctx,
-	h.DB.RO(),
-	db.FindKeyByIdOrHashParams{
-		ID:   sql.NullString{String: req.KeyId, Valid: true},
-		Hash: sql.NullString{String: "", Valid: false},
-	},
-)
+key, err := db.Query.FindKeyByID(ctx, h.DB.RO(), req.KeyId)

140-147: Same issue with permission ID vs slug handling

This handler has the same limitation as the set permissions handler - it only handles permission slugs, not IDs, despite the PR objective to support both.

go/apps/api/openapi/openapi-generated.yaml (6)

540-545: Description still says “slug” only – update to “slug or ID”.

The surrounding paragraph (lines 533-540) clearly states that either identifier works, but the items.description field still mentions “slug” only.
Keeping the docs consistent avoids confusing API consumers.


887-894: Same slug-only wording as earlier – please harmonise.

items.description should mention “slug or ID” to match the behaviour described a few lines above.


955-964: keyId pattern still too permissive + outdated wording.

  1. Pattern ^[a-zA-Z0-9_]+$ accepts fooBar – it should enforce the mandatory key_ prefix (see prior comment).
  2. The permission array description later in this block says “slug” only.

2108-2110: Doc string should mention “slugs or IDs”.

The list now accepts both, but the description still says “permission slugs”.


1204-1208: Clarify that either slug or ID is accepted here too.

The items block for permissions no longer restricts the character set to slugs, but the surrounding description omits IDs. Align wording for accuracy.


2352-2361: Permission.id pattern contradicts its own description.

Description says “Always begins with perm_”, yet pattern ^[a-zA-Z0-9_]+$ does not require it.

-                    pattern: "^[a-zA-Z0-9_]+$"
+                    pattern: "^perm_[a-zA-Z0-9_]+$"
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 8c76365 and 36eb06f.

📒 Files selected for processing (87)
  • go/apps/api/openapi/gen.go (19 hunks)
  • go/apps/api/openapi/openapi-generated.yaml (22 hunks)
  • go/apps/api/openapi/spec/common/role.yaml (0 hunks)
  • go/apps/api/openapi/spec/paths/v2/keys/addRoles/V2KeysAddRolesRequestBody.yaml (1 hunks)
  • go/apps/api/openapi/spec/paths/v2/keys/addRoles/V2KeysAddRolesResponseData.yaml (1 hunks)
  • go/apps/api/openapi/spec/paths/v2/keys/removeRoles/V2KeysRemoveRolesRequestBody.yaml (1 hunks)
  • go/apps/api/openapi/spec/paths/v2/keys/removeRoles/V2KeysRemoveRolesResponseData.yaml (1 hunks)
  • go/apps/api/openapi/spec/paths/v2/keys/setRoles/V2KeysSetRolesRequestBody.yaml (1 hunks)
  • go/apps/api/openapi/spec/paths/v2/keys/setRoles/V2KeysSetRolesResponseData.yaml (1 hunks)
  • go/apps/api/openapi/spec/paths/v2/permissions/deleteRole/V2PermissionsDeleteRoleRequestBody.yaml (1 hunks)
  • go/apps/api/openapi/spec/paths/v2/permissions/getRole/V2PermissionsGetRoleRequestBody.yaml (1 hunks)
  • go/apps/api/routes/v2_apis_list_keys/handler.go (1 hunks)
  • go/apps/api/routes/v2_keys_add_permissions/400_test.go (6 hunks)
  • go/apps/api/routes/v2_keys_add_permissions/401_test.go (2 hunks)
  • go/apps/api/routes/v2_keys_add_permissions/403_test.go (3 hunks)
  • go/apps/api/routes/v2_keys_add_permissions/404_test.go (4 hunks)
  • go/apps/api/routes/v2_keys_add_permissions/handler.go (6 hunks)
  • go/apps/api/routes/v2_keys_add_roles/200_test.go (4 hunks)
  • go/apps/api/routes/v2_keys_add_roles/400_test.go (3 hunks)
  • go/apps/api/routes/v2_keys_add_roles/401_test.go (1 hunks)
  • go/apps/api/routes/v2_keys_add_roles/403_test.go (4 hunks)
  • go/apps/api/routes/v2_keys_add_roles/404_test.go (11 hunks)
  • go/apps/api/routes/v2_keys_add_roles/handler.go (8 hunks)
  • go/apps/api/routes/v2_keys_create_key/handler.go (6 hunks)
  • go/apps/api/routes/v2_keys_remove_permissions/200_test.go (10 hunks)
  • go/apps/api/routes/v2_keys_remove_permissions/400_test.go (2 hunks)
  • go/apps/api/routes/v2_keys_remove_permissions/404_test.go (7 hunks)
  • go/apps/api/routes/v2_keys_remove_roles/200_test.go (4 hunks)
  • go/apps/api/routes/v2_keys_remove_roles/400_test.go (9 hunks)
  • go/apps/api/routes/v2_keys_remove_roles/401_test.go (6 hunks)
  • go/apps/api/routes/v2_keys_remove_roles/403_test.go (7 hunks)
  • go/apps/api/routes/v2_keys_remove_roles/404_test.go (8 hunks)
  • go/apps/api/routes/v2_keys_remove_roles/handler.go (8 hunks)
  • go/apps/api/routes/v2_keys_set_permissions/200_test.go (14 hunks)
  • go/apps/api/routes/v2_keys_set_permissions/401_test.go (2 hunks)
  • go/apps/api/routes/v2_keys_set_permissions/403_test.go (2 hunks)
  • go/apps/api/routes/v2_keys_set_permissions/404_test.go (4 hunks)
  • go/apps/api/routes/v2_keys_set_permissions/handler.go (6 hunks)
  • go/apps/api/routes/v2_keys_set_roles/200_test.go (7 hunks)
  • go/apps/api/routes/v2_keys_set_roles/400_test.go (0 hunks)
  • go/apps/api/routes/v2_keys_set_roles/401_test.go (1 hunks)
  • go/apps/api/routes/v2_keys_set_roles/403_test.go (2 hunks)
  • go/apps/api/routes/v2_keys_set_roles/404_test.go (14 hunks)
  • go/apps/api/routes/v2_keys_set_roles/handler.go (5 hunks)
  • go/apps/api/routes/v2_keys_update_key/handler.go (8 hunks)
  • go/apps/api/routes/v2_permissions_create_permission/handler.go (4 hunks)
  • go/apps/api/routes/v2_permissions_create_role/handler.go (4 hunks)
  • go/apps/api/routes/v2_permissions_delete_permission/200_test.go (5 hunks)
  • go/apps/api/routes/v2_permissions_delete_permission/403_test.go (4 hunks)
  • go/apps/api/routes/v2_permissions_delete_permission/404_test.go (5 hunks)
  • go/apps/api/routes/v2_permissions_delete_role/200_test.go (4 hunks)
  • go/apps/api/routes/v2_permissions_delete_role/400_test.go (2 hunks)
  • go/apps/api/routes/v2_permissions_delete_role/401_test.go (1 hunks)
  • go/apps/api/routes/v2_permissions_delete_role/403_test.go (2 hunks)
  • go/apps/api/routes/v2_permissions_delete_role/404_test.go (2 hunks)
  • go/apps/api/routes/v2_permissions_delete_role/handler.go (2 hunks)
  • go/apps/api/routes/v2_permissions_get_permission/200_test.go (4 hunks)
  • go/apps/api/routes/v2_permissions_get_permission/403_test.go (4 hunks)
  • go/apps/api/routes/v2_permissions_get_role/200_test.go (6 hunks)
  • go/apps/api/routes/v2_permissions_get_role/400_test.go (1 hunks)
  • go/apps/api/routes/v2_permissions_get_role/401_test.go (1 hunks)
  • go/apps/api/routes/v2_permissions_get_role/403_test.go (2 hunks)
  • go/apps/api/routes/v2_permissions_get_role/404_test.go (1 hunks)
  • go/apps/api/routes/v2_permissions_get_role/handler.go (3 hunks)
  • go/apps/api/routes/v2_permissions_list_permissions/200_test.go (4 hunks)
  • go/apps/api/routes/v2_permissions_list_permissions/403_test.go (3 hunks)
  • go/apps/api/routes/v2_permissions_list_roles/200_test.go (2 hunks)
  • go/apps/api/routes/v2_permissions_list_roles/handler.go (2 hunks)
  • go/pkg/db/key_role_delete_many_by_key_and_role_ids.sql_generated.go (1 hunks)
  • go/pkg/db/models_generated.go (2 hunks)
  • go/pkg/db/permission_insert.sql_generated.go (2 hunks)
  • go/pkg/db/plugins/bulk-insert/template_test.go (4 hunks)
  • go/pkg/db/querier_generated.go (7 hunks)
  • go/pkg/db/queries/key_role_delete_many_by_key_and_role_ids.sql (1 hunks)
  • go/pkg/db/queries/role_find_by_id_or_name_with_perms.sql (1 hunks)
  • go/pkg/db/queries/role_find_many_by_id_or_name_with_perms.sql (1 hunks)
  • go/pkg/db/queries/role_find_many_by_name_with_perms.sql (1 hunks)
  • go/pkg/db/queries/role_list.sql (1 hunks)
  • go/pkg/db/queries/role_list_by_key_id.sql (1 hunks)
  • go/pkg/db/role_find_by_id_or_name_with_perms.sql_generated.go (1 hunks)
  • go/pkg/db/role_find_many_by_id_or_name_with_perms.sql_generated.go (1 hunks)
  • go/pkg/db/role_find_many_by_name_with_perms.sql_generated.go (1 hunks)
  • go/pkg/db/role_list.sql_generated.go (2 hunks)
  • go/pkg/db/role_list_by_key_id.sql_generated.go (1 hunks)
  • go/pkg/db/sqlc.json (1 hunks)
  • go/pkg/db/types/null_string.go (1 hunks)
  • go/pkg/testutil/seed/seed.go (3 hunks)
💤 Files with no reviewable changes (2)
  • go/apps/api/routes/v2_keys_set_roles/400_test.go
  • go/apps/api/openapi/spec/common/role.yaml
🧰 Additional context used
🧠 Learnings (80)
📓 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#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: ogzhanolguncu
PR: unkeyed/unkey#3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses `roleId` as the property name for the role identifier, not `id`. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.
Learnt from: chronark
PR: unkeyed/unkey#2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In `apps/api/src/routes/v1_keys_updateKey.ts`, the code intentionally handles `externalId` and `ownerId` separately for clarity. The `ownerId` field will be removed in the future, simplifying the code.
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.
Learnt from: chronark
PR: unkeyed/unkey#3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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.
go/apps/api/openapi/spec/paths/v2/keys/removeRoles/V2KeysRemoveRolesRequestBody.yaml (5)

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.

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

Learnt from: chronark
PR: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

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.

Learnt from: AkshayBandi027
PR: #2215
File: apps/dashboard/app/(app)/@breadcrumb/authorization/roles/[roleId]/page.tsx:28-29
Timestamp: 2024-10-08T15:33:04.290Z
Learning: In authorization/roles/[roleId]/update-role.tsx, the tag role-${role.id} is revalidated after updating a role to ensure that the caching mechanism is properly handled for roles.

go/apps/api/routes/v2_keys_add_permissions/404_test.go (9)

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: #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: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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.

Learnt from: chronark
PR: #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: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: When querying or updating namespaces in the Unkey dashboard, always scope the operations to the current workspace using eq(table.workspaceId, ctx.workspace.id) to prevent cross-workspace access.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use ctx.workspace.id directly instead of fetching the workspace separately for better performance and security.

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.

Learnt from: MichaelUnkey
PR: #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.

go/apps/api/openapi/spec/paths/v2/permissions/getRole/V2PermissionsGetRoleRequestBody.yaml (3)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

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.

Learnt from: AkshayBandi027
PR: #2215
File: apps/dashboard/app/(app)/@breadcrumb/authorization/roles/[roleId]/page.tsx:28-29
Timestamp: 2024-10-08T15:33:04.290Z
Learning: In authorization/roles/[roleId]/update-role.tsx, the tag role-${role.id} is revalidated after updating a role to ensure that the caching mechanism is properly handled for roles.

go/apps/api/routes/v2_keys_add_permissions/403_test.go (8)

Learnt from: MichaelUnkey
PR: #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: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

Learnt from: Flo4604
PR: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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.

Learnt from: chronark
PR: #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: 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.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use ctx.workspace.id directly instead of fetching the workspace separately for better performance and security.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: When querying or updating namespaces in the Unkey dashboard, always scope the operations to the current workspace using eq(table.workspaceId, ctx.workspace.id) to prevent cross-workspace access.

go/apps/api/openapi/spec/paths/v2/keys/setRoles/V2KeysSetRolesRequestBody.yaml (5)

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.

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

Learnt from: chronark
PR: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

Learnt from: AkshayBandi027
PR: #2215
File: apps/dashboard/app/(app)/@breadcrumb/authorization/roles/[roleId]/page.tsx:28-29
Timestamp: 2024-10-08T15:33:04.290Z
Learning: In authorization/roles/[roleId]/update-role.tsx, the tag role-${role.id} is revalidated after updating a role to ensure that the caching mechanism is properly handled for roles.

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.

go/apps/api/routes/v2_keys_set_permissions/401_test.go (2)

Learnt from: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

Learnt from: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

go/pkg/db/key_role_delete_many_by_key_and_role_ids.sql_generated.go (1)

Learnt from: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

go/apps/api/routes/v2_keys_add_roles/handler.go (6)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

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.

Learnt from: ogzhanolguncu
PR: #3661
File: go/apps/api/routes/v2_identities_update_identity/handler.go:115-119
Timestamp: 2025-07-28T11:47:43.144Z
Learning: The v2 update identity endpoint (go/apps/api/routes/v2_identities_update_identity/handler.go) intentionally uses ExternalId field instead of the unified Identity field used in other v2 identity endpoints. This is because the update endpoint needs to both find by externalId and potentially update the externalId value, making the specific field name more appropriate than the generic Identity field.

Learnt from: chronark
PR: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

Learnt from: Flo4604
PR: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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.

Learnt from: chronark
PR: #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_add_roles/400_test.go (5)

Learnt from: MichaelUnkey
PR: #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: #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: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: When querying or updating namespaces in the Unkey dashboard, always scope the operations to the current workspace using eq(table.workspaceId, ctx.workspace.id) to prevent cross-workspace access.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use ctx.workspace.id directly instead of fetching the workspace separately for better performance and security.

go/apps/api/routes/v2_permissions_list_roles/200_test.go (2)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

Learnt from: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

go/apps/api/routes/v2_apis_list_keys/handler.go (4)

Learnt from: ogzhanolguncu
PR: #3661
File: go/apps/api/routes/v2_identities_update_identity/handler.go:115-119
Timestamp: 2025-07-28T11:47:43.144Z
Learning: The v2 update identity endpoint (go/apps/api/routes/v2_identities_update_identity/handler.go) intentionally uses ExternalId field instead of the unified Identity field used in other v2 identity endpoints. This is because the update endpoint needs to both find by externalId and potentially update the externalId value, making the specific field name more appropriate than the generic Identity field.

Learnt from: chronark
PR: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

Learnt from: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

Learnt from: Flo4604
PR: #3151
File: go/apps/api/openapi/gen.go:221-233
Timestamp: 2025-04-18T20:01:33.812Z
Learning: For identity deletion operations in the Unkey API, identityId takes precedence over externalId when both are provided in the request body.

go/apps/api/routes/v2_permissions_get_role/403_test.go (3)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

Learnt from: ogzhanolguncu
PR: #3661
File: go/apps/api/routes/v2_identities_update_identity/handler.go:115-119
Timestamp: 2025-07-28T11:47:43.144Z
Learning: The v2 update identity endpoint (go/apps/api/routes/v2_identities_update_identity/handler.go) intentionally uses ExternalId field instead of the unified Identity field used in other v2 identity endpoints. This is because the update endpoint needs to both find by externalId and potentially update the externalId value, making the specific field name more appropriate than the generic Identity field.

Learnt from: AkshayBandi027
PR: #2215
File: apps/dashboard/app/(app)/@breadcrumb/authorization/roles/[roleId]/page.tsx:28-29
Timestamp: 2024-10-08T15:33:04.290Z
Learning: In authorization/roles/[roleId]/update-role.tsx, the tag role-${role.id} is revalidated after updating a role to ensure that the caching mechanism is properly handled for roles.

go/pkg/db/permission_insert.sql_generated.go (4)

Learnt from: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

Learnt from: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

Learnt from: chronark
PR: #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: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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/apps/api/routes/v2_permissions_delete_role/403_test.go (3)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

Learnt from: AkshayBandi027
PR: #2215
File: apps/dashboard/app/(app)/@breadcrumb/authorization/roles/[roleId]/page.tsx:28-29
Timestamp: 2024-10-08T15:33:04.290Z
Learning: In authorization/roles/[roleId]/update-role.tsx, the tag role-${role.id} is revalidated after updating a role to ensure that the caching mechanism is properly handled for roles.

Learnt from: ogzhanolguncu
PR: #3661
File: go/apps/api/routes/v2_identities_update_identity/handler.go:115-119
Timestamp: 2025-07-28T11:47:43.144Z
Learning: The v2 update identity endpoint (go/apps/api/routes/v2_identities_update_identity/handler.go) intentionally uses ExternalId field instead of the unified Identity field used in other v2 identity endpoints. This is because the update endpoint needs to both find by externalId and potentially update the externalId value, making the specific field name more appropriate than the generic Identity field.

go/apps/api/routes/v2_permissions_get_role/401_test.go (4)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

Learnt from: AkshayBandi027
PR: #2215
File: apps/dashboard/app/(app)/@breadcrumb/authorization/roles/[roleId]/page.tsx:28-29
Timestamp: 2024-10-08T15:33:04.290Z
Learning: In authorization/roles/[roleId]/update-role.tsx, the tag role-${role.id} is revalidated after updating a role to ensure that the caching mechanism is properly handled for roles.

Learnt from: ogzhanolguncu
PR: #3661
File: go/apps/api/routes/v2_identities_update_identity/handler.go:115-119
Timestamp: 2025-07-28T11:47:43.144Z
Learning: The v2 update identity endpoint (go/apps/api/routes/v2_identities_update_identity/handler.go) intentionally uses ExternalId field instead of the unified Identity field used in other v2 identity endpoints. This is because the update endpoint needs to both find by externalId and potentially update the externalId value, making the specific field name more appropriate than the generic Identity field.

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.

go/pkg/db/queries/key_role_delete_many_by_key_and_role_ids.sql (1)

Learnt from: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

go/apps/api/openapi/spec/paths/v2/keys/setRoles/V2KeysSetRolesResponseData.yaml (2)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

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.

go/apps/api/routes/v2_permissions_get_role/404_test.go (3)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

Learnt from: AkshayBandi027
PR: #2215
File: apps/dashboard/app/(app)/@breadcrumb/authorization/roles/[roleId]/page.tsx:28-29
Timestamp: 2024-10-08T15:33:04.290Z
Learning: In authorization/roles/[roleId]/update-role.tsx, the tag role-${role.id} is revalidated after updating a role to ensure that the caching mechanism is properly handled for roles.

Learnt from: ogzhanolguncu
PR: #3661
File: go/apps/api/routes/v2_identities_update_identity/handler.go:115-119
Timestamp: 2025-07-28T11:47:43.144Z
Learning: The v2 update identity endpoint (go/apps/api/routes/v2_identities_update_identity/handler.go) intentionally uses ExternalId field instead of the unified Identity field used in other v2 identity endpoints. This is because the update endpoint needs to both find by externalId and potentially update the externalId value, making the specific field name more appropriate than the generic Identity field.

go/apps/api/routes/v2_keys_set_roles/401_test.go (3)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

Learnt from: ogzhanolguncu
PR: #3661
File: go/apps/api/routes/v2_identities_update_identity/handler.go:115-119
Timestamp: 2025-07-28T11:47:43.144Z
Learning: The v2 update identity endpoint (go/apps/api/routes/v2_identities_update_identity/handler.go) intentionally uses ExternalId field instead of the unified Identity field used in other v2 identity endpoints. This is because the update endpoint needs to both find by externalId and potentially update the externalId value, making the specific field name more appropriate than the generic Identity field.

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.

go/apps/api/routes/v2_keys_remove_roles/403_test.go (2)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

Learnt from: ogzhanolguncu
PR: #3661
File: go/apps/api/routes/v2_identities_update_identity/handler.go:115-119
Timestamp: 2025-07-28T11:47:43.144Z
Learning: The v2 update identity endpoint (go/apps/api/routes/v2_identities_update_identity/handler.go) intentionally uses ExternalId field instead of the unified Identity field used in other v2 identity endpoints. This is because the update endpoint needs to both find by externalId and potentially update the externalId value, making the specific field name more appropriate than the generic Identity field.

go/apps/api/routes/v2_permissions_delete_permission/404_test.go (3)

Learnt from: Flo4604
PR: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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.

Learnt from: chronark
PR: #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: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

go/apps/api/routes/v2_permissions_list_roles/handler.go (2)

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.

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

go/apps/api/routes/v2_permissions_create_role/handler.go (4)

Learnt from: Flo4604
PR: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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.

Learnt from: chronark
PR: #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: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

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.

go/apps/api/routes/v2_keys_add_roles/401_test.go (1)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

go/apps/api/routes/v2_permissions_list_permissions/200_test.go (4)

Learnt from: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

Learnt from: Flo4604
PR: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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.

Learnt from: chronark
PR: #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: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

go/apps/api/routes/v2_permissions_list_permissions/403_test.go (4)

Learnt from: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

Learnt from: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

Learnt from: chronark
PR: #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: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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/apps/api/routes/v2_permissions_delete_role/401_test.go (4)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

Learnt from: AkshayBandi027
PR: #2215
File: apps/dashboard/app/(app)/@breadcrumb/authorization/roles/[roleId]/page.tsx:28-29
Timestamp: 2024-10-08T15:33:04.290Z
Learning: In authorization/roles/[roleId]/update-role.tsx, the tag role-${role.id} is revalidated after updating a role to ensure that the caching mechanism is properly handled for roles.

Learnt from: ogzhanolguncu
PR: #3661
File: go/apps/api/routes/v2_identities_update_identity/handler.go:115-119
Timestamp: 2025-07-28T11:47:43.144Z
Learning: The v2 update identity endpoint (go/apps/api/routes/v2_identities_update_identity/handler.go) intentionally uses ExternalId field instead of the unified Identity field used in other v2 identity endpoints. This is because the update endpoint needs to both find by externalId and potentially update the externalId value, making the specific field name more appropriate than the generic Identity field.

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.

go/apps/api/routes/v2_permissions_delete_role/200_test.go (2)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

Learnt from: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

go/pkg/db/plugins/bulk-insert/template_test.go (4)

Learnt from: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

Learnt from: Flo4604
PR: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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.

Learnt from: chronark
PR: #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: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

go/apps/api/routes/v2_permissions_delete_role/400_test.go (3)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

Learnt from: AkshayBandi027
PR: #2215
File: apps/dashboard/app/(app)/@breadcrumb/authorization/roles/[roleId]/page.tsx:28-29
Timestamp: 2024-10-08T15:33:04.290Z
Learning: In authorization/roles/[roleId]/update-role.tsx, the tag role-${role.id} is revalidated after updating a role to ensure that the caching mechanism is properly handled for roles.

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.

go/apps/api/routes/v2_permissions_get_role/400_test.go (4)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

Learnt from: AkshayBandi027
PR: #2215
File: apps/dashboard/app/(app)/@breadcrumb/authorization/roles/[roleId]/page.tsx:28-29
Timestamp: 2024-10-08T15:33:04.290Z
Learning: In authorization/roles/[roleId]/update-role.tsx, the tag role-${role.id} is revalidated after updating a role to ensure that the caching mechanism is properly handled for roles.

Learnt from: ogzhanolguncu
PR: #3661
File: go/apps/api/routes/v2_identities_update_identity/handler.go:115-119
Timestamp: 2025-07-28T11:47:43.144Z
Learning: The v2 update identity endpoint (go/apps/api/routes/v2_identities_update_identity/handler.go) intentionally uses ExternalId field instead of the unified Identity field used in other v2 identity endpoints. This is because the update endpoint needs to both find by externalId and potentially update the externalId value, making the specific field name more appropriate than the generic Identity field.

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.

go/pkg/db/sqlc.json (2)

Learnt from: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

Learnt from: chronark
PR: #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_permissions_delete_role/handler.go (4)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

Learnt from: AkshayBandi027
PR: #2215
File: apps/dashboard/app/(app)/@breadcrumb/authorization/roles/[roleId]/page.tsx:28-29
Timestamp: 2024-10-08T15:33:04.290Z
Learning: In authorization/roles/[roleId]/update-role.tsx, the tag role-${role.id} is revalidated after updating a role to ensure that the caching mechanism is properly handled for roles.

Learnt from: ogzhanolguncu
PR: #3661
File: go/apps/api/routes/v2_identities_update_identity/handler.go:115-119
Timestamp: 2025-07-28T11:47:43.144Z
Learning: The v2 update identity endpoint (go/apps/api/routes/v2_identities_update_identity/handler.go) intentionally uses ExternalId field instead of the unified Identity field used in other v2 identity endpoints. This is because the update endpoint needs to both find by externalId and potentially update the externalId value, making the specific field name more appropriate than the generic Identity field.

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.

go/apps/api/routes/v2_permissions_get_permission/403_test.go (3)

Learnt from: Flo4604
PR: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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.

Learnt from: chronark
PR: #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: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

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

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.

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use ctx.workspace.id directly instead of fetching the workspace separately for better performance and security.

go/apps/api/routes/v2_keys_add_roles/403_test.go (3)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

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.

Learnt from: ogzhanolguncu
PR: #3661
File: go/apps/api/routes/v2_identities_update_identity/handler.go:115-119
Timestamp: 2025-07-28T11:47:43.144Z
Learning: The v2 update identity endpoint (go/apps/api/routes/v2_identities_update_identity/handler.go) intentionally uses ExternalId field instead of the unified Identity field used in other v2 identity endpoints. This is because the update endpoint needs to both find by externalId and potentially update the externalId value, making the specific field name more appropriate than the generic Identity field.

go/apps/api/openapi/spec/paths/v2/keys/removeRoles/V2KeysRemoveRolesResponseData.yaml (4)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

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.

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.

Learnt from: AkshayBandi027
PR: #2215
File: apps/dashboard/app/(app)/@breadcrumb/authorization/roles/[roleId]/page.tsx:28-29
Timestamp: 2024-10-08T15:33:04.290Z
Learning: In authorization/roles/[roleId]/update-role.tsx, the tag role-${role.id} is revalidated after updating a role to ensure that the caching mechanism is properly handled for roles.

go/pkg/db/queries/role_list.sql (1)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

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

Learnt from: Flo4604
PR: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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.

Learnt from: chronark
PR: #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: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

go/apps/api/routes/v2_keys_update_key/handler.go (12)

Learnt from: chronark
PR: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: #2146
File: apps/dashboard/lib/trpc/routers/api/setDefaultPrefix.ts:80-80
Timestamp: 2024-10-04T17:27:08.666Z
Learning: Ensure that audit log descriptions accurately reflect the action being performed, such as updating the defaultPrefix, and avoid incorrect references like 'name' when not applicable.

Learnt from: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

Learnt from: ogzhanolguncu
PR: #3661
File: go/apps/api/routes/v2_identities_update_identity/handler.go:115-119
Timestamp: 2025-07-28T11:47:43.144Z
Learning: The v2 update identity endpoint (go/apps/api/routes/v2_identities_update_identity/handler.go) intentionally uses ExternalId field instead of the unified Identity field used in other v2 identity endpoints. This is because the update endpoint needs to both find by externalId and potentially update the externalId value, making the specific field name more appropriate than the generic Identity field.

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.

Learnt from: AkshayBandi027
PR: #2215
File: apps/dashboard/app/(app)/@breadcrumb/authorization/roles/[roleId]/page.tsx:28-29
Timestamp: 2024-10-08T15:33:04.290Z
Learning: In authorization/roles/[roleId]/update-role.tsx, the tag role-${role.id} is revalidated after updating a role to ensure that the caching mechanism is properly handled for roles.

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.

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

Learnt from: ogzhanolguncu
PR: #2825
File: apps/dashboard/app/(app)/logs-v2/hooks/use-bookmarked-filters.ts:0-0
Timestamp: 2025-01-30T20:51:44.359Z
Learning: The user (ogzhanolguncu) prefers to handle refactoring suggestions in separate PRs to maintain focus in the current PR.

Learnt from: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

Learnt from: Flo4604
PR: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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/apps/api/routes/v2_keys_remove_roles/400_test.go (7)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

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.

Learnt from: ogzhanolguncu
PR: #3661
File: go/apps/api/routes/v2_identities_update_identity/handler.go:115-119
Timestamp: 2025-07-28T11:47:43.144Z
Learning: The v2 update identity endpoint (go/apps/api/routes/v2_identities_update_identity/handler.go) intentionally uses ExternalId field instead of the unified Identity field used in other v2 identity endpoints. This is because the update endpoint needs to both find by externalId and potentially update the externalId value, making the specific field name more appropriate than the generic Identity field.

Learnt from: chronark
PR: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: When querying or updating namespaces in the Unkey dashboard, always scope the operations to the current workspace using eq(table.workspaceId, ctx.workspace.id) to prevent cross-workspace access.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use ctx.workspace.id directly instead of fetching the workspace separately for better performance and security.

Learnt from: MichaelUnkey
PR: #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.

go/apps/api/routes/v2_keys_remove_roles/401_test.go (6)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

Learnt from: ogzhanolguncu
PR: #3661
File: go/apps/api/routes/v2_identities_update_identity/handler.go:115-119
Timestamp: 2025-07-28T11:47:43.144Z
Learning: The v2 update identity endpoint (go/apps/api/routes/v2_identities_update_identity/handler.go) intentionally uses ExternalId field instead of the unified Identity field used in other v2 identity endpoints. This is because the update endpoint needs to both find by externalId and potentially update the externalId value, making the specific field name more appropriate than the generic Identity field.

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.

Learnt from: AkshayBandi027
PR: #2215
File: apps/dashboard/app/(app)/@breadcrumb/authorization/roles/[roleId]/page.tsx:28-29
Timestamp: 2024-10-08T15:33:04.290Z
Learning: In authorization/roles/[roleId]/update-role.tsx, the tag role-${role.id} is revalidated after updating a role to ensure that the caching mechanism is properly handled for roles.

Learnt from: chronark
PR: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use ctx.workspace.id directly instead of fetching the workspace separately for better performance and security.

go/pkg/db/queries/role_list_by_key_id.sql (3)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

Learnt from: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

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.

go/apps/api/routes/v2_keys_add_permissions/401_test.go (7)

Learnt from: MichaelUnkey
PR: #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: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

Learnt from: Flo4604
PR: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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.

Learnt from: chronark
PR: #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: 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.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use ctx.workspace.id directly instead of fetching the workspace separately for better performance and security.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: When querying or updating namespaces in the Unkey dashboard, always scope the operations to the current workspace using eq(table.workspaceId, ctx.workspace.id) to prevent cross-workspace access.

go/apps/api/routes/v2_permissions_delete_permission/403_test.go (3)

Learnt from: Flo4604
PR: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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.

Learnt from: chronark
PR: #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: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

go/apps/api/routes/v2_keys_set_permissions/403_test.go (10)

Learnt from: MichaelUnkey
PR: #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: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: CR
PR: unkeyed/unkey#0
File: go/deploy/CLAUDE.md:0-0
Timestamp: 2025-07-21T18:05:58.236Z
Learning: Applies to go/deploy/**/*.{go,js,ts,tsx,py,sh,md,txt,json,yaml,yml,ini,env,conf,html,css,scss,xml,c,h,cpp,java,rb,rs,php,pl,sql} : 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.

Learnt from: CR
PR: unkeyed/unkey#0
File: go/deploy/CLAUDE.md:0-0
Timestamp: 2025-07-21T18:05:58.236Z
Learning: Applies to go/deploy/**/*.{go,js,ts,tsx,py,sh,md,txt,json,yaml,yml,ini,env,conf,html,css,scss,xml,c,h,cpp,java,rb,rs,php,pl,sql} : Do not remove AIDEV-*s without explicit human instruction.

Learnt from: Flo4604
PR: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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.

Learnt from: chronark
PR: #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: 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.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use ctx.workspace.id directly instead of fetching the workspace separately for better performance and security.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: When querying or updating namespaces in the Unkey dashboard, always scope the operations to the current workspace using eq(table.workspaceId, ctx.workspace.id) to prevent cross-workspace access.

go/apps/api/routes/v2_keys_add_permissions/400_test.go (10)

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.

Learnt from: MichaelUnkey
PR: #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: #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: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

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.

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.

Learnt from: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: When querying or updating namespaces in the Unkey dashboard, always scope the operations to the current workspace using eq(table.workspaceId, ctx.workspace.id) to prevent cross-workspace access.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use ctx.workspace.id directly instead of fetching the workspace separately for better performance and security.

go/apps/api/routes/v2_keys_remove_permissions/400_test.go (7)

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.

Learnt from: MichaelUnkey
PR: #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: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: #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: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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.

Learnt from: chronark
PR: #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: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

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

Learnt from: Flo4604
PR: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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.

Learnt from: chronark
PR: #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: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

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

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

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.

Learnt from: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

go/apps/api/routes/v2_permissions_create_permission/handler.go (4)

Learnt from: chronark
PR: #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: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

Learnt from: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

Learnt from: Flo4604
PR: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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/apps/api/routes/v2_keys_set_roles/handler.go (7)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

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.

Learnt from: ogzhanolguncu
PR: #3661
File: go/apps/api/routes/v2_identities_update_identity/handler.go:115-119
Timestamp: 2025-07-28T11:47:43.144Z
Learning: The v2 update identity endpoint (go/apps/api/routes/v2_identities_update_identity/handler.go) intentionally uses ExternalId field instead of the unified Identity field used in other v2 identity endpoints. This is because the update endpoint needs to both find by externalId and potentially update the externalId value, making the specific field name more appropriate than the generic Identity field.

Learnt from: chronark
PR: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

Learnt from: AkshayBandi027
PR: #2215
File: apps/dashboard/app/(app)/@breadcrumb/authorization/roles/[roleId]/page.tsx:28-29
Timestamp: 2024-10-08T15:33:04.290Z
Learning: In authorization/roles/[roleId]/update-role.tsx, the tag role-${role.id} is revalidated after updating a role to ensure that the caching mechanism is properly handled for roles.

Learnt from: Flo4604
PR: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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.

Learnt from: chronark
PR: #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_set_permissions/404_test.go (8)

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: Flo4604
PR: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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.

Learnt from: chronark
PR: #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: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: When querying or updating namespaces in the Unkey dashboard, always scope the operations to the current workspace using eq(table.workspaceId, ctx.workspace.id) to prevent cross-workspace access.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use ctx.workspace.id directly instead of fetching the workspace separately for better performance and security.

Learnt from: MichaelUnkey
PR: #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: #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/apps/api/openapi/spec/paths/v2/permissions/deleteRole/V2PermissionsDeleteRoleRequestBody.yaml (3)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

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.

Learnt from: AkshayBandi027
PR: #2215
File: apps/dashboard/app/(app)/@breadcrumb/authorization/roles/[roleId]/page.tsx:28-29
Timestamp: 2024-10-08T15:33:04.290Z
Learning: In authorization/roles/[roleId]/update-role.tsx, the tag role-${role.id} is revalidated after updating a role to ensure that the caching mechanism is properly handled for roles.

go/pkg/testutil/seed/seed.go (3)

Learnt from: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

Learnt from: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

Learnt from: chronark
PR: #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_remove_roles/404_test.go (4)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

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.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: When querying or updating namespaces in the Unkey dashboard, always scope the operations to the current workspace using eq(table.workspaceId, ctx.workspace.id) to prevent cross-workspace access.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use ctx.workspace.id directly instead of fetching the workspace separately for better performance and security.

go/apps/api/routes/v2_permissions_get_role/handler.go (2)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

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.

go/apps/api/routes/v2_keys_set_roles/404_test.go (3)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

Learnt from: ogzhanolguncu
PR: #3661
File: go/apps/api/routes/v2_identities_update_identity/handler.go:115-119
Timestamp: 2025-07-28T11:47:43.144Z
Learning: The v2 update identity endpoint (go/apps/api/routes/v2_identities_update_identity/handler.go) intentionally uses ExternalId field instead of the unified Identity field used in other v2 identity endpoints. This is because the update endpoint needs to both find by externalId and potentially update the externalId value, making the specific field name more appropriate than the generic Identity field.

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.

go/apps/api/routes/v2_keys_remove_permissions/404_test.go (6)

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: #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: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: When querying or updating namespaces in the Unkey dashboard, always scope the operations to the current workspace using eq(table.workspaceId, ctx.workspace.id) to prevent cross-workspace access.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use ctx.workspace.id directly instead of fetching the workspace separately for better performance and security.

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.

go/pkg/db/types/null_string.go (1)

Learnt from: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

go/apps/api/routes/v2_permissions_delete_role/404_test.go (3)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

Learnt from: AkshayBandi027
PR: #2215
File: apps/dashboard/app/(app)/@breadcrumb/authorization/roles/[roleId]/page.tsx:28-29
Timestamp: 2024-10-08T15:33:04.290Z
Learning: In authorization/roles/[roleId]/update-role.tsx, the tag role-${role.id} is revalidated after updating a role to ensure that the caching mechanism is properly handled for roles.

Learnt from: ogzhanolguncu
PR: #3661
File: go/apps/api/routes/v2_identities_update_identity/handler.go:115-119
Timestamp: 2025-07-28T11:47:43.144Z
Learning: The v2 update identity endpoint (go/apps/api/routes/v2_identities_update_identity/handler.go) intentionally uses ExternalId field instead of the unified Identity field used in other v2 identity endpoints. This is because the update endpoint needs to both find by externalId and potentially update the externalId value, making the specific field name more appropriate than the generic Identity field.

go/apps/api/routes/v2_keys_set_permissions/200_test.go (8)

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.

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: #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: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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.

Learnt from: chronark
PR: #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: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: When querying or updating namespaces in the Unkey dashboard, always scope the operations to the current workspace using eq(table.workspaceId, ctx.workspace.id) to prevent cross-workspace access.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use ctx.workspace.id directly instead of fetching the workspace separately for better performance and security.

go/apps/api/openapi/spec/paths/v2/keys/addRoles/V2KeysAddRolesResponseData.yaml (3)

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.

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

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.

go/apps/api/routes/v2_keys_create_key/handler.go (6)

Learnt from: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

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.

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.

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

Learnt from: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

go/apps/api/routes/v2_keys_add_roles/404_test.go (4)

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.

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: When querying or updating namespaces in the Unkey dashboard, always scope the operations to the current workspace using eq(table.workspaceId, ctx.workspace.id) to prevent cross-workspace access.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use ctx.workspace.id directly instead of fetching the workspace separately for better performance and security.

go/pkg/db/models_generated.go (2)

Learnt from: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

Learnt from: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

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

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

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.

Learnt from: MichaelUnkey
PR: #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.

go/apps/api/routes/v2_keys_remove_permissions/200_test.go (7)

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: #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: #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: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

Learnt from: Flo4604
PR: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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.

Learnt from: chronark
PR: #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: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

go/pkg/db/role_find_many_by_id_or_name_with_perms.sql_generated.go (1)

Learnt from: chronark
PR: #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/openapi/spec/paths/v2/keys/addRoles/V2KeysAddRolesRequestBody.yaml (6)

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.

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

Learnt from: chronark
PR: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

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.

Learnt from: AkshayBandi027
PR: #2215
File: apps/dashboard/app/(app)/@breadcrumb/authorization/roles/[roleId]/page.tsx:28-29
Timestamp: 2024-10-08T15:33:04.290Z
Learning: In authorization/roles/[roleId]/update-role.tsx, the tag role-${role.id} is revalidated after updating a role to ensure that the caching mechanism is properly handled for roles.

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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.

go/apps/api/routes/v2_keys_set_roles/403_test.go (4)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

Learnt from: AkshayBandi027
PR: #2215
File: apps/dashboard/app/(app)/@breadcrumb/authorization/roles/[roleId]/page.tsx:28-29
Timestamp: 2024-10-08T15:33:04.290Z
Learning: In authorization/roles/[roleId]/update-role.tsx, the tag role-${role.id} is revalidated after updating a role to ensure that the caching mechanism is properly handled for roles.

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.

Learnt from: ogzhanolguncu
PR: #3661
File: go/apps/api/routes/v2_identities_update_identity/handler.go:115-119
Timestamp: 2025-07-28T11:47:43.144Z
Learning: The v2 update identity endpoint (go/apps/api/routes/v2_identities_update_identity/handler.go) intentionally uses ExternalId field instead of the unified Identity field used in other v2 identity endpoints. This is because the update endpoint needs to both find by externalId and potentially update the externalId value, making the specific field name more appropriate than the generic Identity field.

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

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

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.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use ctx.workspace.id directly instead of fetching the workspace separately for better performance and security.

go/pkg/db/role_list_by_key_id.sql_generated.go (2)

Learnt from: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

go/apps/api/routes/v2_keys_remove_roles/handler.go (8)

Learnt from: chronark
PR: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

Learnt from: ogzhanolguncu
PR: #3661
File: go/apps/api/routes/v2_identities_update_identity/handler.go:115-119
Timestamp: 2025-07-28T11:47:43.144Z
Learning: The v2 update identity endpoint (go/apps/api/routes/v2_identities_update_identity/handler.go) intentionally uses ExternalId field instead of the unified Identity field used in other v2 identity endpoints. This is because the update endpoint needs to both find by externalId and potentially update the externalId value, making the specific field name more appropriate than the generic Identity field.

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.

Learnt from: Flo4604
PR: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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.

Learnt from: chronark
PR: #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: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use ctx.workspace.id directly instead of fetching the workspace separately for better performance and security.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: When querying or updating namespaces in the Unkey dashboard, always scope the operations to the current workspace using eq(table.workspaceId, ctx.workspace.id) to prevent cross-workspace access.

go/apps/api/routes/v2_keys_add_permissions/handler.go (7)

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.

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.

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: Flo4604
PR: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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.

Learnt from: chronark
PR: #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: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

Learnt from: Flo4604
PR: #3606
File: go/pkg/prometheus/metrics/database.go:29-30
Timestamp: 2025-07-16T10:06:35.397Z
Learning: In Go packages, variables defined in one file within a package (like latencyBuckets and constLabels in go/pkg/prometheus/metrics/http.go) are accessible from other files in the same package without requiring imports. This is a common pattern for sharing configuration across multiple files within a package.

go/apps/api/routes/v2_keys_set_permissions/handler.go (6)

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.

Learnt from: ogzhanolguncu
PR: #3661
File: go/apps/api/routes/v2_identities_update_identity/handler.go:115-119
Timestamp: 2025-07-28T11:47:43.144Z
Learning: The v2 update identity endpoint (go/apps/api/routes/v2_identities_update_identity/handler.go) intentionally uses ExternalId field instead of the unified Identity field used in other v2 identity endpoints. This is because the update endpoint needs to both find by externalId and potentially update the externalId value, making the specific field name more appropriate than the generic Identity field.

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.

Learnt from: chronark
PR: #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: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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.

Learnt from: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

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

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.

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.

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: CR
PR: unkeyed/unkey#0
File: go/deploy/CLAUDE.md:0-0
Timestamp: 2025-07-21T18:05:58.236Z
Learning: Applies to go/deploy/**/*.{go,js,ts,tsx,py,sh,md,txt,json,yaml,yml,ini,env,conf,html,css,scss,xml,c,h,cpp,java,rb,rs,php,pl,sql} : Do not remove AIDEV-*s without explicit human instruction.

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

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.

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.

Learnt from: Flo4604
PR: #3647
File: go/apps/api/openapi/openapi-generated.yaml:3569-3575
Timestamp: 2025-07-22T18:09:41.800Z
Learning: In the Unkey codebase, using non-standard HTTP status code 529 for internal-only endpoints is acceptable and should not be flagged as an issue in future reviews.

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.

Learnt from: CR
PR: unkeyed/unkey#0
File: go/deploy/CLAUDE.md:0-0
Timestamp: 2025-07-21T18:05:58.236Z
Learning: Applies to go/deploy/**/*.{env,sh,yaml,yml,json,conf,ini} : All environment variables MUST follow the format UNKEY_<SERVICE_NAME>_VARNAME.

Learnt from: CR
PR: unkeyed/unkey#0
File: go/deploy/CLAUDE.md:0-0
Timestamp: 2025-07-21T18:05:58.236Z
Learning: Applies to go/deploy/**/*.{go,js,ts,tsx,py,sh,md,txt,json,yaml,yml,ini,env,conf,html,css,scss,xml,c,h,cpp,java,rb,rs,php,pl,sql} : 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.

go/pkg/db/querier_generated.go (4)

Learnt from: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

Learnt from: chronark
PR: #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: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use ctx.workspace.id directly instead of fetching the workspace separately for better performance and security.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: When querying or updating namespaces in the Unkey dashboard, always scope the operations to the current workspace using eq(table.workspaceId, ctx.workspace.id) to prevent cross-workspace access.

go/apps/api/openapi/openapi-generated.yaml (16)

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.

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.

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: #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: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

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.

Learnt from: CR
PR: unkeyed/unkey#0
File: go/deploy/CLAUDE.md:0-0
Timestamp: 2025-07-21T18:05:58.236Z
Learning: Applies to go/deploy/**/*.{go,js,ts,tsx,py,sh,md,txt,json,yaml,yml,ini,env,conf,html,css,scss,xml,c,h,cpp,java,rb,rs,php,pl,sql} : Do not remove AIDEV-*s without explicit human instruction.

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

Learnt from: CR
PR: unkeyed/unkey#0
File: go/deploy/CLAUDE.md:0-0
Timestamp: 2025-07-21T18:05:58.236Z
Learning: Applies to go/deploy/**/*.{go,js,ts,tsx,py,sh,md,txt,json,yaml,yml,ini,env,conf,html,css,scss,xml,c,h,cpp,java,rb,rs,php,pl,sql} : 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.

Learnt from: MichaelUnkey
PR: #3173
File: apps/docs/security/delete-protection.mdx:32-36
Timestamp: 2025-04-22T17:33:28.162Z
Learning: In the Unkey dashboard UI for delete protection, the button/link to initiate the process is labeled "Disable Delete Protection" while the confirmation button is labeled "Disable API Delete Protection". The documentation should maintain these different labels to match the actual UI.

Learnt from: MichaelUnkey
PR: #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: ogzhanolguncu
PR: #3661
File: go/apps/api/routes/v2_identities_update_identity/handler.go:115-119
Timestamp: 2025-07-28T11:47:43.144Z
Learning: The v2 update identity endpoint (go/apps/api/routes/v2_identities_update_identity/handler.go) intentionally uses ExternalId field instead of the unified Identity field used in other v2 identity endpoints. This is because the update endpoint needs to both find by externalId and potentially update the externalId value, making the specific field name more appropriate than the generic Identity field.

Learnt from: chronark
PR: #2146
File: apps/dashboard/lib/trpc/routers/api/setDefaultPrefix.ts:80-80
Timestamp: 2024-10-04T17:27:08.666Z
Learning: Ensure that audit log descriptions accurately reflect the action being performed, such as updating the defaultPrefix, and avoid incorrect references like 'name' when not applicable.

Learnt from: MichaelUnkey
PR: #3173
File: apps/docs/security/delete-protection.mdx:21-24
Timestamp: 2025-04-22T17:34:04.438Z
Learning: In the Unkey dashboard UI for enabling delete protection, the button/link to initiate the process is labeled "Enable Delete Protection" while the confirmation button is labeled "Enable API Delete Protection". The documentation should maintain these different labels to match the actual UI.

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

Learnt from: AkshayBandi027
PR: #2215
File: apps/dashboard/app/(app)/@breadcrumb/authorization/roles/[roleId]/page.tsx:28-29
Timestamp: 2024-10-08T15:33:04.290Z
Learning: In authorization/roles/[roleId]/update-role.tsx, the tag role-${role.id} is revalidated after updating a role to ensure that the caching mechanism is properly handled for roles.

🧬 Code Graph Analysis (30)
go/apps/api/routes/v2_permissions_list_roles/200_test.go (1)
go/pkg/db/types/null_string.go (1)
  • NullString (10-10)
go/apps/api/routes/v2_permissions_get_role/403_test.go (1)
go/apps/api/openapi/gen.go (1)
  • Role (425-449)
go/pkg/db/permission_insert.sql_generated.go (1)
go/pkg/db/types/null_string.go (1)
  • NullString (10-10)
go/apps/api/routes/v2_permissions_delete_role/403_test.go (1)
go/apps/api/openapi/gen.go (1)
  • Role (425-449)
go/apps/api/routes/v2_permissions_get_role/404_test.go (1)
go/apps/api/openapi/gen.go (1)
  • Role (425-449)
go/apps/api/routes/v2_permissions_delete_permission/404_test.go (3)
go/apps/api/openapi/gen.go (1)
  • Permission (296-316)
go/pkg/db/models_generated.go (1)
  • Permission (746-754)
go/pkg/db/types/null_string.go (1)
  • NullString (10-10)
go/apps/api/routes/v2_permissions_list_permissions/200_test.go (2)
go/pkg/db/types/null_string.go (1)
  • NullString (10-10)
go/pkg/cli/flag.go (1)
  • String (331-363)
go/apps/api/routes/v2_permissions_list_permissions/403_test.go (1)
go/pkg/db/types/null_string.go (1)
  • NullString (10-10)
go/apps/api/routes/v2_permissions_delete_role/401_test.go (1)
go/apps/api/openapi/gen.go (1)
  • Role (425-449)
go/apps/api/routes/v2_permissions_delete_role/200_test.go (3)
go/pkg/db/types/null_string.go (1)
  • NullString (10-10)
go/apps/api/openapi/gen.go (1)
  • Role (425-449)
go/pkg/db/models_generated.go (1)
  • Role (813-820)
go/apps/api/routes/v2_permissions_delete_role/400_test.go (2)
go/apps/api/routes/v2_permissions_delete_role/handler.go (1)
  • Request (19-19)
go/apps/api/openapi/gen.go (1)
  • Role (425-449)
go/apps/api/routes/v2_permissions_delete_role/handler.go (5)
go/pkg/db/role_find_by_id_or_name_with_perms.sql_generated.go (1)
  • FindRoleByIdOrNameWithPermsParams (36-39)
go/apps/api/openapi/gen.go (1)
  • Role (425-449)
go/pkg/db/models_generated.go (2)
  • Role (813-820)
  • AuditLog (554-570)
go/pkg/fault/wrap.go (3)
  • Wrap (25-67)
  • Internal (75-89)
  • Public (97-111)
go/pkg/codes/unkey_application.go (1)
  • App (53-71)
go/apps/api/routes/v2_permissions_delete_permission/200_test.go (3)
go/pkg/db/types/null_string.go (1)
  • NullString (10-10)
go/apps/api/openapi/gen.go (1)
  • Permission (296-316)
go/pkg/db/models_generated.go (1)
  • Permission (746-754)
go/apps/api/routes/v2_keys_add_permissions/401_test.go (4)
go/pkg/testutil/seed/seed.go (3)
  • CreateApiRequest (79-87)
  • CreateKeyRequest (183-200)
  • New (36-42)
go/pkg/uid/uid.go (1)
  • TestPrefix (24-24)
go/pkg/db/permission_insert.sql_generated.go (1)
  • InsertPermissionParams (33-40)
go/pkg/db/types/null_string.go (1)
  • NullString (10-10)
go/apps/api/routes/v2_permissions_delete_permission/403_test.go (2)
go/apps/api/openapi/gen.go (1)
  • Permission (296-316)
go/pkg/db/models_generated.go (1)
  • Permission (746-754)
go/apps/api/routes/v2_keys_add_permissions/400_test.go (4)
go/pkg/db/types/null_string.go (1)
  • NullString (10-10)
go/apps/api/routes/v2_keys_add_permissions/handler.go (1)
  • Request (25-25)
go/pkg/testutil/seed/seed.go (1)
  • New (36-42)
go/pkg/uid/uid.go (2)
  • TestPrefix (24-24)
  • KeyPrefix (16-16)
go/apps/api/routes/v2_permissions_get_permission/200_test.go (6)
go/pkg/uid/uid.go (1)
  • PermissionPrefix (27-27)
go/pkg/db/permission_insert.sql_generated.go (1)
  • InsertPermissionParams (33-40)
go/pkg/db/types/null_string.go (1)
  • NullString (10-10)
go/apps/api/openapi/gen.go (1)
  • Permission (296-316)
go/pkg/db/models_generated.go (1)
  • Permission (746-754)
go/pkg/testutil/http.go (1)
  • CallRoute (257-291)
go/apps/api/routes/v2_permissions_get_role/200_test.go (3)
go/pkg/db/types/null_string.go (1)
  • NullString (10-10)
go/apps/api/openapi/gen.go (1)
  • Role (425-449)
go/pkg/db/models_generated.go (1)
  • Role (813-820)
go/apps/api/routes/v2_permissions_create_permission/handler.go (1)
go/pkg/db/types/null_string.go (1)
  • NullString (10-10)
go/apps/api/routes/v2_keys_set_permissions/404_test.go (3)
go/pkg/db/types/null_string.go (1)
  • NullString (10-10)
go/pkg/testutil/seed/seed.go (1)
  • New (36-42)
go/pkg/uid/uid.go (1)
  • KeyPrefix (16-16)
go/apps/api/routes/v2_keys_remove_roles/404_test.go (3)
go/pkg/db/role_insert.sql_generated.go (1)
  • InsertRoleParams (30-36)
go/pkg/db/types/null_string.go (1)
  • NullString (10-10)
go/apps/api/routes/v2_keys_remove_roles/handler.go (1)
  • Request (23-23)
go/apps/api/routes/v2_permissions_delete_role/404_test.go (1)
go/apps/api/openapi/gen.go (1)
  • Role (425-449)
go/apps/api/routes/v2_keys_create_key/handler.go (4)
go/apps/api/openapi/gen.go (1)
  • Permission (296-316)
go/pkg/db/models_generated.go (1)
  • Permission (746-754)
go/pkg/db/permission_insert.sql_generated.go (1)
  • InsertPermissionParams (33-40)
go/pkg/db/types/null_string.go (1)
  • NullString (10-10)
go/pkg/db/role_list.sql_generated.go (2)
go/pkg/db/types/null_string.go (1)
  • NullString (10-10)
go/pkg/hydra/store/db.go (1)
  • DBTX (8-13)
go/apps/api/routes/v2_keys_add_roles/200_test.go (2)
go/pkg/testutil/seed/seed.go (1)
  • CreateRoleRequest (360-366)
go/pkg/ptr/pointer.go (1)
  • P (49-51)
go/pkg/db/role_find_by_id_or_name_with_perms.sql_generated.go (2)
go/pkg/db/types/null_string.go (1)
  • NullString (10-10)
go/pkg/hydra/store/db.go (1)
  • DBTX (8-13)
go/apps/api/routes/v2_keys_set_roles/200_test.go (3)
go/pkg/rbac/query.go (1)
  • T (84-90)
internal/db/src/types.ts (1)
  • InsertRole (26-26)
go/pkg/db/role_insert.sql_generated.go (1)
  • InsertRoleParams (30-36)
go/pkg/db/role_list_by_key_id.sql_generated.go (2)
go/pkg/db/types/null_string.go (1)
  • NullString (10-10)
go/pkg/hydra/store/db.go (1)
  • DBTX (8-13)
go/apps/api/openapi/gen.go (2)
go/pkg/codes/unkey_data.go (1)
  • Data (87-128)
go/pkg/db/models_generated.go (2)
  • Permission (746-754)
  • Role (813-820)
go/pkg/db/querier_generated.go (11)
go/pkg/hydra/store/db.go (1)
  • DBTX (8-13)
go/pkg/db/key_permission_delete_many_by_key_and_permission_ids.sql_generated.go (1)
  • DeleteManyKeyPermissionByKeyAndPermissionIDsParams (18-21)
go/pkg/db/key_role_delete_many_by_key_and_role_ids.sql_generated.go (1)
  • DeleteManyKeyRolesByKeyAndRoleIDsParams (18-21)
go/pkg/db/role_find_many_by_id_or_name_with_perms.sql_generated.go (2)
  • FindManyRolesByIdOrNameWithPermsParams (37-40)
  • FindManyRolesByIdOrNameWithPermsRow (42-50)
go/pkg/db/role_find_many_by_name_with_perms.sql_generated.go (2)
  • FindManyRolesByNamesWithPermsParams (34-37)
  • FindManyRolesByNamesWithPermsRow (39-47)
go/pkg/db/permission_find_by_id_or_slug.sql_generated.go (1)
  • FindPermissionByIdOrSlugParams (18-21)
go/pkg/db/models_generated.go (1)
  • Permission (746-754)
go/pkg/db/permission_find_by_slugs.sql_generated.go (1)
  • FindPermissionsBySlugsParams (17-20)
go/pkg/db/role_find_by_id_or_name_with_perms.sql_generated.go (2)
  • FindRoleByIdOrNameWithPermsParams (36-39)
  • FindRoleByIdOrNameWithPermsRow (41-49)
go/pkg/db/role_list.sql_generated.go (2)
  • ListRolesParams (36-39)
  • ListRolesRow (41-49)
go/pkg/db/role_list_by_key_id.sql_generated.go (1)
  • ListRolesByKeyIDRow (35-43)
⏰ 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). (3)
  • GitHub Check: Test Go API Local / Test
  • GitHub Check: Test Agent Local / test_agent_local
  • GitHub Check: Test API / API Test Local
🔇 Additional comments (198)
go/pkg/db/plugins/bulk-insert/template_test.go (4)

28-28: LGTM! Good addition of ValuesFields for template rendering.

The new ValuesFields field properly separates the fields used in the SQL VALUES clause from other template fields, enabling more flexible bulk insert operations with upsert capabilities.


51-51: LGTM! Consistent ValuesFields usage in upsert test case.

The ValuesFields addition aligns with the upsert functionality where fields for VALUES clause and ON DUPLICATE KEY UPDATE clause may need to be handled separately.


71-71: LGTM! ValuesFields properly added to no-db-argument test case.

Consistent with other test cases, ensuring the template handles both database connection patterns correctly.


113-113: LGTM! ValuesFields included in error handling test.

Ensures the invalid template test covers the complete TemplateData structure including the new ValuesFields field.

go/apps/api/routes/v2_permissions_get_role/403_test.go (2)

56-56: LGTM! Field name change aligns with unified API model.

The change from RoleId to Role reflects the API model update that allows endpoints to accept either role IDs or names, providing more flexibility in role management.


83-83: LGTM! Consistent field naming with API model changes.

The Role field name matches the updated API specification and maintains the same functionality while supporting the unified role identifier approach.

go/apps/api/routes/v2_permissions_delete_role/403_test.go (2)

62-62: LGTM! Consistent field naming with API model updates.

The change from RoleId to Role aligns with the unified API approach that accepts either role IDs or names, maintaining consistency across permission and role management endpoints.


86-86: LGTM! Field name change supports flexible role identification.

The Role field name matches the updated API specification and supports the enhanced flexibility in role management operations.

go/apps/api/routes/v2_permissions_list_roles/200_test.go (2)

14-14: LGTM! Import added for custom nullable string type.

The addition of the dbtype package import supports the migration to custom nullable string handling for better JSON and database operations.


61-61: LGTM! Migration to custom nullable string type for permissions.

The change from sql.NullString to dbtype.NullString for permission descriptions aligns with the codebase migration to unified nullable string handling. This provides better JSON serialization and database handling for permission metadata.

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

189-189: LGTM! Minor formatting improvement enhances readability.

The added blank line properly separates the error handling block from the subsequent logic, improving code organization and readability.

go/apps/api/routes/v2_permissions_delete_role/404_test.go (1)

48-48: LGTM: Field rename aligns with API flexibility enhancement.

The change from RoleId to Role correctly reflects the API's enhanced capability to accept either role IDs or slugs, as mentioned in the PR objectives.

Also applies to: 83-83

go/apps/api/routes/v2_permissions_get_role/401_test.go (1)

26-26: LGTM: Consistent field rename for API unification.

The field rename from RoleId to Role is consistent with the broader API refactoring to support both IDs and slugs.

go/apps/api/routes/v2_permissions_list_permissions/403_test.go (2)

14-14: LGTM: Standardizing nullable string type usage.

The addition of the dbtype import alias supports the project's move to custom nullable string types instead of the standard library's sql.NullString.


40-40: LGTM: Consistent nullable string type update.

The change from sql.NullString to dbtype.NullString aligns with the project-wide type standardization effort, maintaining the same functionality while using the custom type system.

Also applies to: 81-81

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

38-42: LGTM: Comprehensive and consistent terminology updates.

Excellent work updating not just the field names but also the comments and test case names to consistently use "role" instead of "roleId". This level of consistency should be the standard across all related files.

Also applies to: 57-61

go/pkg/db/queries/key_role_delete_many_by_key_and_role_ids.sql (1)

1-3: LGTM! Well-structured batch deletion query.

The SQL query correctly implements batch deletion of key-role associations using sqlc conventions. The use of sqlc.slice('role_ids') with the IN operator will efficiently handle multiple role deletions in a single database operation, which aligns with the PR's objective to improve batch operations.

go/apps/api/routes/v2_permissions_delete_role/401_test.go (1)

27-27: LGTM! Field name updated to match API changes.

The field name change from RoleId to Role aligns with the PR's objective to allow either role ID or slug in permission-related requests. The test value follows the expected role ID format.

go/apps/api/routes/v2_keys_set_roles/401_test.go (1)

29-29: LGTM! Simplified roles field structure.

The change from complex role objects to a simple string slice ([]string{"role_123"}) aligns with the API simplification goals. This supports the PR's objective to accept either role ID or slug as string values, making the API more flexible and easier to use.

go/apps/api/routes/v2_keys_remove_roles/403_test.go (1)

69-69: LGTM! Consistent roles field simplification across all test cases.

The simplification of the Roles field from complex objects to string slices is applied consistently across all authorization test cases. This change supports the PR's goal of accepting either role ID or slug as simple string values, improving API usability while maintaining comprehensive test coverage.

Also applies to: 122-122, 175-175, 228-228, 281-281, 334-334, 388-388

go/apps/api/routes/v2_keys_add_roles/401_test.go (1)

51-51: LGTM! Roles field simplified for better API usability.

The change from complex role objects to a simple string slice ([]string{"role_123"}) is consistent with the API refactoring across the codebase. This simplification supports the PR's objective to accept either role ID or slug as string values, making the API more intuitive while maintaining full test coverage for authentication scenarios.

go/apps/api/routes/v2_permissions_get_permission/403_test.go (3)

14-14: LGTM!

The import of the custom dbtype package aligns with the codebase migration from standard library sql.NullString to the project-specific nullable string type.


43-43: LGTM!

The migration from sql.NullString to dbtype.NullString for the permission description field is consistent with the broader codebase standardization effort.


59-59: LGTM!

The field name change from PermissionId to Permission simplifies the request structure and aligns with the PR objective to allow either permission ID or slug in permission-related requests.

Also applies to: 86-86

go/pkg/db/models_generated.go (2)

13-13: LGTM!

The import addition enables the use of the custom dbtype.NullString type in the generated database models, supporting the migration from standard library types.


751-751: LGTM!

The migration of the Description field from sql.NullString to dbtype.NullString in the generated Permission struct aligns with the codebase-wide standardization of nullable string handling.

go/pkg/db/permission_insert.sql_generated.go (2)

11-11: LGTM!

The import addition enables the use of the custom dbtype.NullString type in the generated permission insertion parameters.


38-38: LGTM!

The migration of the Description field to dbtype.NullString in the InsertPermissionParams struct maintains consistency with the Permission model and supports unified nullable string handling across database operations.

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

14-14: LGTM!

The import of the custom dbtype package enables the test to use the project-specific nullable string type for permission descriptions.


68-68: LGTM!

The migration to dbtype.NullString for the permission description in test data creation maintains consistency with the updated database models.


112-112: LGTM!

The field name change from RoleId to Role simplifies the request structure and supports the API's flexibility to accept either role ID or name.

Also applies to: 171-171

go/apps/api/routes/v2_permissions_get_role/404_test.go (1)

40-40: LGTM!

The field name change from RoleId to Role aligns with the API simplification effort to use unified identifier fields that can accept either role ID or name.

go/apps/api/routes/v2_permissions_create_role/handler.go (4)

6-6: LGTM!

The addition of the fmt import is necessary for the fmt.Sprintf usage in the error message formatting below.


92-92: Improved error message formatting with quoted role names.

The use of fmt.Sprintf with quoted role names (%q) provides better readability and clarity in error messages, making it easier to identify the specific role name that caused the duplicate error.


110-110: Excellent use of audit log constants.

Replacing the hardcoded string with auditlog.RoleCreateEvent improves maintainability and consistency across the codebase. This aligns with the broader refactoring to standardize audit log event types.


120-120: Consistent use of audit log resource type constants.

Using auditlog.RoleResourceType instead of hardcoded strings ensures consistency with other audit log implementations and makes the code more maintainable.

go/apps/api/openapi/spec/paths/v2/keys/addRoles/V2KeysAddRolesResponseData.yaml (1)

16-16: Excellent schema consolidation.

Replacing the inline role object definition with a reference to the common role schema (../../../../common/role.yaml) eliminates duplication and creates a single source of truth for role definitions. This improves maintainability and consistency across the API specification.

go/apps/api/routes/v2_permissions_list_permissions/200_test.go (2)

13-13: Good refactoring to use custom nullable string type.

The import change to use dbtype alias for the custom nullable string type is part of a broader codebase standardization that centralizes nullable string handling and enables custom JSON marshaling behavior.


63-63: Consistent migration to dbtype.NullString.

The replacement of sql.NullString with dbtype.NullString for permission descriptions is consistent throughout the test and aligns with the broader refactoring to standardize nullable string types across the codebase. This enables better JSON handling and maintains type safety.

Also applies to: 76-76, 150-150

go/apps/api/routes/v2_keys_set_roles/403_test.go (1)

69-69: Excellent API simplification.

The change from a slice of structs with optional Id and Name pointers to a simple slice of role ID strings significantly simplifies the request payload structure. This makes the API more intuitive and easier to use while maintaining the same functionality.

This aligns with the broader refactoring to standardize role and permission representations across the codebase.

Also applies to: 97-97

go/pkg/db/sqlc.json (1)

44-52: Proper sqlc configuration for custom nullable string type.

The override configuration correctly maps the permissions.description database column to the custom dbtype.NullString type with the appropriate package import. This ensures that generated database code uses the consistent nullable string type across the codebase, enabling custom JSON marshaling behavior while maintaining proper nullable handling.

go/apps/api/routes/v2_keys_set_permissions/401_test.go (3)

15-15: LGTM: Adopting custom nullable string type.

The migration from database/sql to dbtype aligns with the project-wide standardization of nullable string handling.


75-75: LGTM: Consistent nullable string type usage.

The change from sql.NullString to dbtype.NullString maintains consistency with the database model updates and improves type safety.


80-81: LGTM: Simplified API model for permissions.

The change from complex permission objects to a simple string slice ([]string{permissionID}) aligns with the API simplification goals mentioned in the PR objectives. This makes the API more intuitive for users who can now specify permissions by either ID or slug directly.

go/apps/api/routes/v2_keys_add_roles/403_test.go (4)

60-60: LGTM: Simplified role specification in API.

The change from complex role objects to a simple string slice ([]string{"role_123"}) aligns with the API model simplification. Users can now specify roles by either ID or name directly, making the API more intuitive.


124-124: LGTM: Consistent role specification pattern.

The simplified string slice format for roles maintains consistency across all test cases and aligns with the unified API model.


170-170: LGTM: Maintains test coverage with simplified API.

The role specification change preserves the authorization test logic while using the cleaner API model.


216-216: LGTM: Final role specification follows pattern.

Consistent with other test cases, the simplified role format improves API usability.

go/apps/api/routes/v2_permissions_delete_permission/403_test.go (4)

14-14: LGTM: Consistent nullable string type adoption.

The import of dbtype package aligns with the project-wide migration from sql.NullString to dbtype.NullString.


44-44: LGTM: Database model consistency.

The change to dbtype.NullString maintains consistency with the updated database models and improves nullable string handling.


60-60: LGTM: Unified permission field naming.

The change from PermissionId to Permission aligns with the API model unification where the field now accepts either permission ID or slug, as mentioned in the PR objectives.


95-95: LGTM: Consistent permission field usage.

The Permission field usage is consistent across test cases and supports the unified API model.

go/pkg/db/queries/role_list.sql (1)

2-21: LGTM: Enhanced role query with permission aggregation.

The SQL query enhancement effectively aggregates permissions for each role using JSON functions:

  • JSON_ARRAYAGG creates a properly structured array of permission objects
  • COALESCE ensures an empty array when no permissions exist, preventing null values
  • Pagination support with id_cursor and limit of 101 (standard pattern for has_more logic)
  • The JOIN and subquery structure is efficient and maintains data integrity

This optimization reduces the need for N+1 queries when fetching roles with their associated permissions.

go/apps/api/routes/v2_permissions_delete_permission/200_test.go (5)

13-13: LGTM: Consistent type import.

The dbtype import maintains consistency with the project-wide migration to custom nullable string types.


55-55: LGTM: Database type consistency.

The migration to dbtype.NullString aligns with the updated database models and maintains consistency across the codebase.


67-67: LGTM: Unified permission field naming.

The change from PermissionId to Permission supports the API's ability to accept either permission ID or slug, as outlined in the PR objectives.


113-113: LGTM: Consistent nullable string usage.

The dbtype.NullString usage maintains consistency with the database model updates.


126-126: LGTM: Consistent permission field usage.

The Permission field usage aligns with the unified API model across all test scenarios.

go/pkg/testutil/seed/seed.go (3)

14-14: LGTM!

The import addition for the custom nullable string type aligns with the codebase standardization effort.


152-152: LGTM!

The replacement of sql.NullString with dbtype.NullString for the permission description field is consistent with the broader codebase refactoring to standardize nullable string handling.


415-415: LGTM!

Consistent usage of dbtype.NullString for the permission description field, matching the pattern established throughout the codebase.

go/pkg/db/queries/role_find_many_by_name_with_perms.sql (1)

1-18: Efficient batch query design.

This SQL query correctly implements batch role retrieval with permissions aggregation. The design is well-structured:

  • Proper workspace scoping ensures data isolation
  • JSON aggregation provides a consistent response format with COALESCE handling empty cases
  • The batch approach is more efficient than individual role lookups
  • All relevant permission fields are included in the JSON object

The use of sqlc.slice('names') for dynamic IN clause generation is appropriate for the sqlc workflow.

go/apps/api/openapi/spec/paths/v2/keys/removeRoles/V2KeysRemoveRolesResponseData.yaml (1)

17-17: LGTM!

The reference to the common role schema (../../../../common/role.yaml) is a good approach for API schema unification, reducing duplication and improving maintainability. The comprehensive description clearly explains the response behavior and expectations.

go/pkg/db/queries/role_find_by_id_or_name_with_perms.sql (1)

1-21: Flexible role lookup implementation.

This query provides efficient role retrieval by either ID or name with permissions aggregation. The implementation is well-designed:

  • Consistent JSON aggregation pattern with the batch query
  • Flexible OR condition allows lookup by either ID or name using a single parameter
  • Proper workspace scoping maintains data isolation
  • COALESCE ensures consistent response format

The single search parameter approach is clean and efficient for the use case.

go/apps/api/routes/v2_permissions_delete_permission/404_test.go (3)

14-14: LGTM!

The import change to use the custom nullable string type aligns with the codebase standardization effort for consistent type handling.


47-47: Consistent API model updates.

The field name change from PermissionId to Permission across all test cases correctly reflects the updated API model that now accepts either permission IDs or slugs as strings. This simplifies the request structure and aligns with the broader API unification effort.

Also applies to: 68-68, 104-104


93-93: LGTM!

The type change from sql.NullString to dbtype.NullString for the permission description field is consistent with the database model updates and codebase standardization.

go/pkg/db/queries/role_find_many_by_id_or_name_with_perms.sql (1)

1-21: Well-structured query that supports flexible role lookup.

The SQL query effectively implements the PR objective of allowing either permission ID or slug (in this case, role ID or name) by:

  • Using sqlc.slice('search') to match against both r.id and r.name fields
  • Properly aggregating permissions as JSON using JSON_ARRAYAGG and json_object
  • Handling empty permission sets with COALESCE and JSON_ARRAY()
  • Maintaining workspace isolation for security

The JSON aggregation approach eliminates the need for separate permission queries in handlers, improving performance.

go/apps/api/routes/v2_permissions_list_roles/handler.go (3)

5-5: LGTM: Added encoding/json import for permission unmarshaling.

The import is correctly added to support the new JSON unmarshaling approach for role permissions.


91-96: Improved role response initialization with explicit defaults.

The early initialization with explicit nil values for Description and Permissions makes the code more readable and prevents potential nil pointer issues.


116-116: LGTM: Simplified permission assignment using unified response structure.

The direct assignment to roleResponse.Permissions is cleaner than the previous approach and aligns with the unified role schema changes.

go/apps/api/routes/v2_permissions_delete_role/handler.go (3)

66-69: Excellent: Supports flexible role identification as per PR objectives.

The updated query FindRoleByIdOrNameWithPerms enables the handler to accept either role ID or name (slug), directly implementing the PR's core objective. The workspace scoping is now handled at the query level, which is more secure than explicit checks.


84-84: LGTM: Consistent use of resolved role ID.

Using role.ID (the resolved role ID) instead of req.Role for all deletion operations ensures consistency regardless of whether the request used a role ID or name. This prevents potential issues where the request contained a name but deletions expected an ID.

Also applies to: 92-92, 100-100


111-111: Improved maintainability with audit log constants.

Replacing string literals with constants from the auditlog package (auditlog.RoleDeleteEvent, auditlog.RoleResourceType) improves maintainability and prevents typos in audit log entries.

Also applies to: 116-116, 121-122

go/apps/api/openapi/spec/paths/v2/keys/setRoles/V2KeysSetRolesResponseData.yaml (1)

17-17: Excellent: Centralized schema reference improves maintainability.

Replacing the inline role definition with a reference to "../../../../common/role.yaml" is a best practice that:

  • Eliminates schema duplication across endpoints
  • Ensures consistent role representation throughout the API
  • Simplifies future schema updates by maintaining a single source of truth
  • Aligns with the PR's objective of standardizing role and permission handling
go/apps/api/routes/v2_keys_create_key/handler.go (5)

20-20: LGTM: Added dbtype import for unified null string handling.

The import of dbtype package supports the transition from sql.NullString to dbtype.NullString, which is part of the codebase-wide null string type unification mentioned in the PR.


367-370: Improved: Using full Permission struct for consistency.

The change from db.FindPermissionsBySlugsRow to db.Permission provides consistency across the codebase and ensures all permission fields are available for processing.


373-373: Comprehensive permission struct initialization prevents field omission issues.

The updated code properly initializes all fields of the db.Permission struct when creating new permissions, including:

  • All required fields (ID, Name, Slug, WorkspaceID, CreatedAtM)
  • Proper null handling with dbtype.NullString for Description
  • Default values for UpdatedAtM

This prevents potential issues from missing fields and aligns with the codebase-wide type improvements.

Also applies to: 392-400


388-388: Consistent null string type usage.

Using dbtype.NullString instead of sql.NullString for the Description field aligns with the PR's objective of unifying null string handling across the codebase.


435-435: Improved maintainability with audit log resource type constants.

Replacing string literals with constants from the auditlog package (auditlog.KeyResourceType, auditlog.PermissionResourceType, auditlog.RoleResourceType, auditlog.APIResourceType) improves code maintainability and prevents typos in audit log entries.

Also applies to: 442-442, 522-522, 529-529, 564-564, 571-571

go/pkg/db/queries/role_list_by_key_id.sql (1)

2-16: Review JSON aggregation pattern and performance optimization

We’ve verified that the correlated JSON_ARRAYAGG subquery isn’t unique to role_list_by_key_id.sql—it appears in six other queries under go/pkg/db/queries/ (e.g., role_list.sql, role_find_by_id_or_name_with_perms.sql, etc.). While functionally correct, executing a subquery per role can incur an N+1–style cost on large datasets.

Before keeping this pattern, please:

  • Profile this query (e.g., EXPLAIN ANALYZE) against expected data volumes.
  • Ensure there are indexes on roles_permissions.role_id and permissions.id.
  • Consider a single JOIN + GROUP BY approach, for example:
SELECT
  r.*,
  COALESCE(
    JSON_ARRAYAGG(
      JSON_OBJECT(
        'id', p.id,
        'name', p.name,
        'slug', p.slug,
        'description', p.description
      )
    ) FILTER (WHERE p.id IS NOT NULL),
    JSON_ARRAY()
  ) AS permissions
FROM roles r
LEFT JOIN roles_permissions rp ON rp.role_id = r.id
LEFT JOIN permissions p        ON p.id = rp.permission_id
WHERE r.key_id = :key_id
GROUP BY r.id;

This rewrite aggregates all permissions in one pass rather than per role. If benchmarks show no significant difference, the existing pattern is acceptable; otherwise, migrate to the JOIN+GROUP BY style for better scalability.

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

14-14: LGTM: Custom nullable string type migration.

The migration from sql.NullString to dbtype.NullString is consistent with the broader codebase standardization on custom nullable types.


84-84: LGTM: Request field updated to support ID or name.

The change from RoleId to Role aligns with the PR objective to allow either permission ID or slug in permission-related requests. The tests now properly validate both ID-based (line 84) and name-based (line 140) role lookup.

Also applies to: 140-140


163-164: LGTM: Updated permissions assertion for new JSON array structure.

The assertion changes from checking non-nil to verifying empty slice correctly reflect the new permissions representation as a JSON array unmarshaled from the database query.

go/apps/api/routes/v2_keys_add_permissions/401_test.go (4)

13-15: LGTM: Updated imports for test refactoring.

The import changes correctly reflect the migration to helper-based test setup (testutil/seed) and custom nullable types (dbtype), while removing dependencies on manual database operations.


36-53: LGTM: Simplified test setup with helper functions.

The refactoring from manual database insertions to CreateApi and CreateKey helper functions reduces boilerplate code and improves test maintainability while maintaining the same test coverage.


61-67: LGTM: Consistent migration to custom nullable string type.

The change from sql.NullString to dbtype.NullString aligns with the broader codebase standardization on custom nullable types.


65-67: LGTM: Simplified request structure.

The change from a slice of permission structs to a slice of permission ID strings reflects the API model simplification and makes the request construction more straightforward.

go/apps/api/routes/v2_keys_set_permissions/403_test.go (4)

13-15: LGTM: Updated imports for test refactoring.

The import changes correctly support the migration to helper-based test setup and custom nullable types, consistent with other test files in this refactor.


36-61: LGTM: Simplified test setup with helper functions.

The refactoring to use CreateApi and CreateKey helper functions improves test maintainability and consistency across the test suite.


63-63: LGTM: Corrected permission ID prefix.

Good fix changing from uid.TestPrefix to uid.PermissionPrefix - this ensures the permission ID follows the correct naming convention for actual permissions rather than test entities.


69-75: LGTM: Consistent type migration and request simplification.

The changes to use dbtype.NullString and simplify the request structure to use a slice of strings align with the broader API model refactoring and improve consistency.

go/apps/api/routes/v2_keys_remove_roles/401_test.go (1)

66-66: LGTM: Simplified role representation improves API usability.

The change from complex role objects with optional Id and Name fields to simple string arrays is a good simplification. This makes the API more intuitive and easier to use while maintaining the same functionality.

Also applies to: 116-116, 167-167, 218-218, 269-269, 324-324

go/apps/api/routes/v2_keys_set_roles/404_test.go (2)

81-81: LGTM: Consistent role simplification across test cases.

The uniform change from role structs to string arrays maintains consistency across all test scenarios and simplifies the API interface.

Also applies to: 105-105, 129-129, 237-237, 259-259, 284-284, 309-309, 334-334


119-119: Good: Standardized error message format.

The consistent use of fmt.Sprintf("Role %q was not found", ...) provides uniform error messaging across different test scenarios, improving the user experience.

Also applies to: 143-143, 251-251, 273-273, 298-298, 348-348

go/apps/api/routes/v2_permissions_create_permission/handler.go (3)

14-14: LGTM: Standardized internal type usage.

The switch from sql.NullString to dbtype.NullString aligns with the codebase-wide standardization of using internal nullable types for better consistency and type safety.

Also applies to: 78-78


99-99: Good: Constants improve maintainability.

Replacing string literals with predefined constants (auditlog.PermissionCreateEvent and auditlog.PermissionResourceType) reduces the risk of typos and improves maintainability across the audit logging system.

Also applies to: 109-109


111-111: Audit log resource name is correct for permissions
The change in v2_permissions_create_permission/handler.go to set the audit log resource’s Name field to req.Slug (with DisplayName: req.Name) is intentional and appropriate: slugs serve as stable identifiers, while display names remain human-readable. Other permission handlers don’t emit audit logs for names, and roles have no slug field, so they continue to use req.Name. No further changes are needed here.

go/apps/api/routes/v2_keys_add_permissions/400_test.go (3)

15-15: LGTM: Consistent internal type usage.

The standardization to dbtype.NullString maintains consistency with the broader codebase changes for nullable string handling.

Also applies to: 183-183, 253-253


37-37: Good: Updated permission scope for root key.

Adding "rbac.*.add_permission_to_key" permission ensures the root key has the necessary authorization for the updated permission management functionality.


188-189: LGTM: Simplified permission request structure.

The change from complex permission objects to simple string arrays aligns with the API simplification goals and makes the interface more intuitive.

Also applies to: 209-210, 227-228, 259-260

go/apps/api/routes/v2_keys_add_roles/404_test.go (3)

36-36: Good: Updated RBAC permission scope.

Adding "rbac.*.add_role_to_key" ensures the root key has proper authorization for the enhanced role management functionality.


48-48: LGTM: Consistent role simplification.

The uniform change from role objects to string arrays maintains consistency across all test scenarios and simplifies the API interface.

Also applies to: 88-88, 126-126, 164-164, 212-212, 261-261, 311-311, 365-365, 415-415


298-302: Improved test clarity with specific error checking.

The variable name changes (validName, invalidName) and the updated error assertion that checks for the specific invalid role name improve test clarity and make failures easier to debug.

Also applies to: 324-324

go/apps/api/routes/v2_permissions_get_permission/200_test.go (4)

13-13: LGTM! Consistent type migration.

The migration from sql.NullString to dbtype.NullString is consistent with the codebase-wide effort to use a custom nullable string type.


42-84: Well-structured permission retrieval tests.

The test properly validates permission retrieval by ID with all fields populated, including the nullable description field using the new dbtype.NullString type.


86-106: Good addition of slug-based retrieval test.

Adding a separate test case for retrieving permissions by slug ensures both identifier types are properly tested, which aligns with the API's flexibility to accept either ID or slug.


119-119: Consistent nullable type usage.

Both the permission insertion and request use the updated field names and types consistently - dbtype.NullString for the description and Permission (instead of PermissionId) for the request field.

Also applies to: 126-126

go/apps/api/routes/v2_keys_remove_permissions/404_test.go (4)

15-15: LGTM! Consistent type migration.

The migration to dbtype.NullString is consistent with the codebase-wide nullable string type standardization.


38-38: Appropriate permission scope addition.

Adding "rbac.*.remove_permission_from_key" to the root key ensures proper authorization for the remove permissions operation.


60-62: Clean API simplification.

The request structure has been simplified from complex objects with Id and Slug pointers to a straightforward string slice, making the API more intuitive and easier to use.

Also applies to: 98-99, 169-170, 240-241


132-132: Consistent nullable type usage.

All permission insertions now use dbtype.NullString for the description field, maintaining consistency across the test suite.

Also applies to: 235-235

go/apps/api/routes/v2_keys_update_key/handler.go (5)

18-18: LGTM! Consistent type migration.

The import of dbtype package aligns with the codebase-wide migration to use custom nullable types.


110-111: Excellent audit logging implementation.

The audit logging has been enhanced with:

  • Initialization inside the transaction for consistency
  • Detailed entries for each auto-created permission
  • Use of predefined constants for resource types (auditlog.KeyResourceType, auditlog.APIResourceType, auditlog.PermissionResourceType)
  • Comprehensive resource metadata

This provides excellent traceability for permission creation and key updates.

Also applies to: 416-441, 549-575


384-413: Clean permission handling logic.

The permission resolution logic efficiently:

  • Uses a map to track existing permissions
  • Identifies which permissions need to be created
  • Maintains the requested permission list for later assignment

The implementation is clear and performant.


461-469: Proper timestamp handling for bulk operations.

Using a single timestamp for both CreatedAt and UpdatedAt fields ensures consistency in bulk insert operations and aligns with the updated SQL queries that include ON DUPLICATE KEY UPDATE logic.


593-593: Standardized response format.

Using openapi.EmptyResponse{} instead of a pointer to an empty struct follows the standardized empty response pattern across the API.

go/apps/api/openapi/spec/paths/v2/keys/setRoles/V2KeysSetRolesRequestBody.yaml (1)

28-32: Clean API schema simplification.

The role specification has been simplified from a complex object structure to a simple string representing the role name. The pattern ^[a-zA-Z][a-zA-Z0-9_-]*$ ensures valid role names that start with a letter, and the length constraints (3-255) provide reasonable bounds. This makes the API more intuitive and aligns with the broader simplification effort across role and permission endpoints.

go/apps/api/routes/v2_keys_remove_roles/400_test.go (2)

35-35: Appropriate permission scope for role operations.

Adding "rbac.*.remove_role_from_key" permission ensures proper authorization for role removal operations.


86-86: Consistent API request simplification.

All test cases have been updated to use the simplified Roles: []string{...} format instead of complex objects with Id and Name fields. This aligns perfectly with the API schema changes and makes the tests cleaner and more maintainable.

Also applies to: 114-114, 185-185, 222-222, 259-259, 289-289, 335-335, 383-383

go/apps/api/routes/v2_keys_remove_roles/200_test.go (4)

35-35: LGTM! Appropriate permission added for role removal operations.

The addition of "rbac.*.remove_role_from_key" permission is correct and aligns with the enhanced RBAC controls introduced in this PR.


43-43: Test case name accurately reflects the operation.

The rename from a generic test name to "remove single role" provides better clarity about what this specific test case validates.


88-88: Clean API simplification with string-based role references.

The change from complex objects with optional ID/Name fields to a simple string slice improves API usability and aligns with the overall simplification effort across the codebase.

Also applies to: 145-145


129-134: Consistent test data naming.

Using descriptive role names like "unassigned_role" improves test readability and makes the test's purpose clearer.

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

64-80: Efficient single-query approach for role retrieval.

The refactoring to use FindRoleByIdOrNameWithPerms eliminates the N+1 query problem by fetching the role and its permissions in a single database call. This is a good performance optimization.

go/apps/api/routes/v2_keys_set_roles/handler.go (3)

88-102: Enhanced permission verification with API-specific checks.

Good improvement to check permissions at both the wildcard level and the specific API level. This provides more granular access control.


117-136: Efficient batch role resolution and validation.

The refactoring to use FindManyRolesByNamesWithPerms with a combined validation map eliminates multiple database queries and simplifies the validation logic.


198-201: Optimized batch database operations.

Excellent use of batch operations (DeleteManyKeyRolesByKeyAndRoleIDs and BulkQuery.InsertKeyRoles) to reduce database round trips and improve performance.

Also applies to: 251-251

go/apps/api/routes/v2_keys_remove_roles/404_test.go (2)

35-35: Consistent permission addition across test files.

The addition of "rbac.*.remove_role_from_key" permission matches the changes in the success test file, maintaining consistency.


59-59: Simplified test requests align with API changes.

All test cases have been properly updated to use the new string slice format for roles, maintaining comprehensive 404 error coverage while simplifying the test code.

Also applies to: 98-98, 138-138, 188-188, 238-238, 288-288, 338-338

go/apps/api/routes/v2_keys_remove_permissions/400_test.go (2)

13-13: Consistent migration to custom NullString type.

The change from sql.NullString to dbtype.NullString aligns with the codebase-wide type migration for better type safety and consistency.

Also applies to: 167-167


174-175: Simplified permission references in test requests.

The change to use a string slice for permissions matches the API simplification, making the test requests cleaner and more intuitive.

go/apps/api/openapi/spec/paths/v2/permissions/getRole/V2PermissionsGetRoleRequestBody.yaml (2)

3-3: Property rename aligns with API unification.

The rename from roleId to role effectively supports the PR objective of accepting either role ID or role name in a single field.


9-12: Pattern doesn't match role ID format described in documentation.

The regex pattern ^[a-zA-Z][a-zA-Z0-9_-]*$ requires the first character to be a letter, but the description states that role IDs begin with "role_" (which starts with a letter). However, this pattern would reject role IDs like "role_1234567890abcdef" because the underscore appears immediately after the first letter.

Consider updating the pattern to accommodate both formats:

-    pattern: "^[a-zA-Z][a-zA-Z0-9_-]*$"
+    pattern: "^[a-zA-Z][a-zA-Z0-9_-]*$|^role_[a-zA-Z0-9]+$"

Or simplify to allow role IDs by relaxing the first character requirement:

-    pattern: "^[a-zA-Z][a-zA-Z0-9_-]*$"
+    pattern: "^[a-zA-Z0-9][a-zA-Z0-9_-]*$"
⛔ Skipped due to 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#3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: ogzhanolguncu
PR: unkeyed/unkey#3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses `roleId` as the property name for the role identifier, not `id`. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.
go/apps/api/routes/v2_keys_add_roles/400_test.go (2)

30-30: Correct RBAC permission added for role management.

The addition of "rbac.*.add_role_to_key" permission scope is appropriate for the enhanced role management functionality introduced in this PR.


119-122: Simplified role representation aligns with API changes.

The change from complex role objects to a simple string slice ([]string{}) is consistent with the API simplification described in the PR summary.

go/apps/api/openapi/spec/paths/v2/keys/addRoles/V2KeysAddRolesRequestBody.yaml (1)

28-32: Role specification simplified to string type.

The simplification from complex role objects to string identifiers aligns well with the PR's goal to unify role references across the API.

go/pkg/db/key_role_delete_many_by_key_and_role_ids.sql_generated.go (2)

31-38: Proper handling of variable-length role ID slices.

The dynamic query construction correctly handles both populated and empty role ID slices. The NULL replacement for empty slices ensures the SQL query remains syntactically valid while avoiding unintended deletions.


27-41: Well-structured batch deletion method.

The generated method properly implements batch deletion with appropriate parameter binding and error handling. This supports the performance optimization goals mentioned in the PR summary.

go/pkg/db/types/null_string.go (2)

9-10: Clean custom type definition.

Using a type alias based on sql.NullString provides a solid foundation while allowing custom JSON behavior.


35-43: Proper SQL interface delegation.

The Scan and Value methods correctly delegate to the underlying sql.NullString implementation, ensuring database compatibility.

go/apps/api/routes/v2_keys_remove_permissions/200_test.go (5)

13-13: LGTM!

The addition of the dbtype import supports the project's type unification effort, replacing standard sql.NullString with a custom type for consistency.


35-35: Enhanced RBAC permission scope is appropriate.

The addition of "rbac.*.remove_permission_from_key" permission to the root key aligns with the enhanced RBAC permission checking introduced in this PR.


74-74: Excellent API simplification!

The change from complex permission structs with optional Id and Slug pointers to a simple []string significantly improves API usability while maintaining the same functionality. Users can now directly specify permissions as strings without worrying about struct construction.

Also applies to: 152-152, 203-203, 293-293, 393-397


259-259: Consistent type usage.

The migration from sql.NullString to dbtype.NullString aligns with the project-wide type unification effort and provides better type consistency across the codebase.

Also applies to: 270-270


43-417: Comprehensive test coverage.

The test suite effectively covers all key scenarios: single/multiple permission removal, idempotent operations, partial removal, and complete removal. The tests properly verify both API responses and database state changes.

go/apps/api/routes/v2_keys_set_permissions/200_test.go (4)

35-35: Comprehensive RBAC permissions for set operation.

The expanded permission scopes "rbac.*.remove_permission_from_key", "rbac.*.add_permission_to_key", and "rbac.*.create_permission" are appropriate for the set_permissions endpoint, which may need to perform all these operations when setting the complete permission set.


113-113: Consistent API design across endpoints.

The simplified Permissions: []string format maintains consistency with the remove_permissions endpoint, providing a unified developer experience across permission management operations.

Also applies to: 205-205, 291-291, 353-353, 398-398


128-136: Well-designed helper function.

The contains helper function elegantly handles potential ordering differences in response data, making the tests more robust and maintainable.


376-432: Excellent validation of auto-creation feature.

This test case effectively validates the key PR feature of automatically creating permissions when they don't exist, properly verifying both the API response and the resulting database state.

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

34-34: Consistent RBAC enhancement for role operations.

The addition of "rbac.*.add_role_to_key" permission follows the same enhanced RBAC pattern as the permission endpoints, maintaining consistency across the API.


63-63: Consistent API simplification for roles.

The Roles: []string format maintains the same simplification pattern as permissions, providing a unified API experience across both role and permission management operations.

Also applies to: 124-124


87-162: Comprehensive idempotent behavior validation.

This test effectively validates the nuanced behavior of adding both existing and new roles in a single request, properly verifying that only new role assignments generate audit logs while existing ones remain idempotent.

go/apps/api/routes/v2_keys_set_permissions/404_test.go (3)

60-60: Consistent error test structure.

The simplified Permissions: []string format in error tests maintains consistency with the success tests, ensuring comprehensive validation of the API contract.

Also applies to: 110-110


76-124: Critical workspace isolation validation.

This test properly validates multi-tenant security by ensuring that keys from different workspaces are isolated and return appropriate 404 errors, which is essential for preventing unauthorized cross-workspace access.


51-51: Consistent database type usage.

The use of dbtype.NullString maintains type consistency across all test files and supports the project's type unification effort.

Also applies to: 104-104

go/pkg/db/role_list_by_key_id.sql_generated.go (4)

14-33: Efficient SQL query enhancement.

The JSON aggregation of permissions within roles is well-designed, using appropriate MySQL JSON functions and COALESCE to handle edge cases. This approach efficiently avoids N+1 query problems and provides rich data in a single query.


35-43: Well-designed struct for enriched data.

The new ListRolesByKeyIDRow struct appropriately handles the enriched role data with permissions. Using interface{} for the JSON permissions field is a pragmatic choice that provides flexibility for JSON unmarshaling.


66-66: Appropriate method signature update.

The change from []Role to []ListRolesByKeyIDRow properly reflects the enhanced data structure and maintains type safety for the enriched query results.


72-87: Correct scanning logic implementation.

The updated scanning logic properly handles the new Permissions field while maintaining consistent error handling patterns throughout the function.

go/apps/api/routes/v2_keys_add_permissions/404_test.go (4)

14-14: LGTM: Custom nullable string type import.

The addition of the dbtype import supports the migration from sql.NullString to the custom dbtype.NullString type, which is consistent with the broader codebase standardization efforts.


36-36: LGTM: Expanded root key permissions for enhanced RBAC.

The addition of "rbac.*.remove_permission_from_key" permission scope aligns with the enhanced permission management capabilities introduced in this PR, ensuring proper authorization for permission-related operations.


46-63: LGTM: Simplified permission request structure and improved clarity.

The changes effectively implement the PR objective of supporting both permission ID and slug by:

  1. Clearer variable naming: Using permissionSlug variable for better readability
  2. Simplified request format: Converting from complex struct with optional Create, Id, and Slug fields to a straightforward []string slice
  3. Consistent database types: Migration to dbtype.NullString aligns with codebase standardization

The simplified API request format makes the endpoint more intuitive while maintaining the flexibility to accept either IDs or slugs.


112-119: LGTM: Consistent application of refactoring patterns.

The changes maintain consistency with the previous test case by:

  1. Database type consistency: Using dbtype.NullString for the description field
  2. Simplified request structure: Using permission ID directly in the string slice format

This uniform application of the refactoring ensures consistency across all test scenarios.

go/apps/api/routes/v2_keys_add_permissions/403_test.go (4)

13-15: LGTM: Enhanced imports for improved test infrastructure.

The added imports support key improvements:

  1. dbtype: Enables migration to custom nullable string types for better type safety
  2. seed: Provides structured helper methods for test data creation, improving test maintainability

36-76: LGTM: Improved test data creation and simplified request structure.

The refactoring enhances test quality through:

  1. Helper method adoption: Replacing manual SQL inserts with h.CreateApi() and h.CreateKey() improves maintainability and reduces boilerplate
  2. Structured seed requests: Explicit field specification makes test setup clearer and more maintainable
  3. Simplified permission requests: Converting from complex objects to string slices aligns with the PR's goal of supporting ID or slug references
  4. Database type consistency: Migration to dbtype.NullString maintains consistency with codebase standards

121-156: LGTM: Consistent refactoring applied to cross-workspace test.

The changes maintain consistency with the overall refactoring approach:

  1. Helper method usage: h.CreateWorkspace(), h.CreateApi(), and h.CreateKey() provide better abstraction
  2. Structured test data: Explicit field specification improves test clarity
  3. Simplified requests: String slice format for permissions maintains consistency with other test cases

The cross-workspace authorization logic remains intact while benefiting from improved test infrastructure.


151-151: LGTM: Proper RBAC permission configuration.

The addition of "rbac.*.add_permission_to_key" ensures the root key has the necessary permissions for the enhanced permission management operations being tested, maintaining proper authorization scope.

go/apps/api/routes/v2_keys_set_roles/200_test.go (4)

43-75: LGTM: Simplified role request structure improves API usability.

The refactoring from complex role objects to a simple string slice ([]string{roleName}) aligns with the PR objective of simplifying permission and role management. The test logic and assertions remain intact, ensuring continued verification of role assignment functionality.


127-155: LGTM: Improved readability with consistent simplification.

The changes enhance test quality through:

  1. Clearer variable naming: Using roleName variable improves code readability
  2. Consistent request format: String slice format maintains consistency with other test cases
  3. Preserved test logic: Role replacement functionality verification remains intact

242-242: LGTM: Intuitive empty roles representation.

Using an empty string slice ([]string{}) to represent clearing all roles is intuitive and aligns well with the simplified API design. This clearly communicates the intent to remove all role associations.


298-324: LGTM: Consistent pattern applied to idempotent operation test.

The changes maintain the established refactoring pattern:

  1. Clear variable naming: roleName variable improves readability
  2. Simplified request format: String slice maintains consistency
  3. Important edge case: Continues to verify that no audit logs are created when no changes occur

This ensures the API correctly handles idempotent operations with the new simplified format.

go/pkg/db/role_find_by_id_or_name_with_perms.sql_generated.go (2)

13-34: LGTM: Well-designed query supporting flexible role lookup.

The SQL query effectively implements the PR's objective of supporting both ID and name-based role lookup:

  1. Efficient data fetching: JSON aggregation of permissions reduces database round-trips
  2. Flexible lookup: OR condition supports both r.id = ? and r.name = ? searches
  3. Proper scoping: Workspace-scoped queries prevent cross-workspace access
  4. Null handling: COALESCE ensures roles without permissions return empty JSON arrays

36-86: LGTM: Well-structured generated code with efficient parameter design.

The generated code demonstrates good design principles:

  1. Efficient parameters: Single Search field supports both ID and name lookup without duplication
  2. Appropriate types: interface{} for Permissions field correctly handles JSON data
  3. Standard patterns: Generated method follows established sqlc conventions
  4. Proper error handling: Standard error propagation from database operations
go/pkg/db/role_find_many_by_id_or_name_with_perms.sql_generated.go (2)

14-40: LGTM: Proper extension of single role query for batch operations.

The query effectively scales the single role lookup pattern for multiple roles:

  1. Consistent structure: Maintains the JSON aggregation and COALESCE patterns from the single role query
  2. Batch-friendly: Uses IN clauses for efficient multiple role lookup
  3. Flexible matching: Same search terms can match either role IDs or names via OR condition
  4. Proper scoping: Workspace restriction prevents cross-workspace access

78-93: No action needed: duplicate search parameter processing is required.

The two identical blocks correspond to two separate /*SLICE:search*/? placeholders—each must expand the arg.Search slice and append its values in order. This duplication is expected for correctly binding parameters to both IN clauses.

go/apps/api/routes/v2_keys_remove_roles/handler.go (7)

59-65: Good: Explicit null handling for hash parameter

The change to use FindKeyByIdOrHash with explicit sql.NullString handling is cleaner and more explicit than the previous approach. This aligns well with Go's preference for explicit null handling.


79-85: Security improvement: Early workspace validation

Moving the workspace validation before permission checks is a good security practice. This prevents potential information leakage about key existence across workspaces.


115-130: Efficient batch role lookup

The refactoring to use FindManyRolesByNamesWithPerms for batch role lookup is more efficient than individual lookups. The dual-key map approach (by ID and name) is clever for validation.


142-151: Elegant role filtering logic

The logic to filter roles that are currently assigned is clean and efficient. The in-place deletion from currentRoleIDs while building rolesToRemove is a nice optimization.


189-200: Performance improvement: Batch deletion

The change to use DeleteManyKeyRolesByKeyAndRoleIDs for batch deletion is a significant performance improvement over multiple individual deletes.


215-243: Efficient response construction

The optimization to avoid an extra database query by using the remaining roles in currentRoleIDs is excellent. This reduces database load while maintaining correctness.


94-94: Permission scope change is consistent across key handlers

The switch from key.KeyAuthID to using the API’s ID (key.Api.ID) for ResourceID matches all other key-related endpoints (get, update, delete, set/remove roles/permissions). No further action needed here.

go/apps/api/openapi/spec/paths/v2/keys/removeRoles/V2KeysRemoveRolesRequestBody.yaml (1)

20-32: API simplification: String-based role references

The simplification from complex objects to string arrays for role names is a good API design improvement. The pattern validation ^[a-zA-Z][a-zA-Z0-9_-]*$ aligns with the learnings about role name validation at the OpenAPI schema layer.

go/apps/api/routes/v2_keys_add_roles/handler.go (3)

87-110: Comprehensive permission checks

The compound permission check using rbac.And to require both update key permission AND add role permission is excellent security practice. This ensures proper authorization for role management operations.


204-212: Efficient batch insertion

The use of BulkQuery.InsertKeyRoles for batch insertion is more efficient than individual inserts. Good performance optimization.


227-241: Smart response construction

The approach of wrapping newly added roles and appending them to current roles for the response is efficient and avoids an extra database query.

go/pkg/db/role_list.sql_generated.go (1)

14-34: Efficient permission aggregation

The JSON aggregation approach to include permissions with roles in a single query is excellent for performance. The use of COALESCE with JSON_ARRAY() ensures consistent return types even when no permissions exist.

go/pkg/db/role_find_many_by_name_with_perms.sql_generated.go (2)

72-79: Proper handling of dynamic IN clause

The dynamic SQL construction for the IN clause is well-implemented with proper handling of the empty slice case. The approach of replacing with NULL when no names are provided is appropriate.


68-108: Well-structured batch query implementation

This new query method provides efficient batch role lookup with permissions included. The implementation follows sqlc patterns correctly and includes proper error handling and resource cleanup.

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

219-263: Well-implemented batch deletion with comprehensive audit logging

The batch deletion using DeleteManyKeyPermissionByKeyAndPermissionIDs is efficient, and the audit logging properly captures all permission removal events with appropriate metadata.

go/pkg/db/querier_generated.go (1)

42-46: Well-structured database interface improvements

The new batch delete methods, enriched role queries with permissions, and the upsert behavior for key permissions all support more efficient database operations. The changes to return full Permission structs instead of partial data improve type safety and reduce the need for additional queries.

Also applies to: 52-56, 317-339, 340-359, 368-373, 390-393, 809-810, 1186-1206, 1209-1228

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

127-128: Good standardization of empty responses

The introduction of EmptyResponse as a unified type for empty success responses improves API consistency and makes the intent clear in the generated code.


741-743: Permission handling improvements align with PR objectives

The updated permission fields now correctly support both IDs and slugs as strings, with clear documentation about auto-creation behavior when the root key has appropriate permissions. This simplification makes the API more flexible and user-friendly.

Also applies to: 995-999, 1082-1096


1485-1500: Consistent field naming for ID/slug flexibility

The renaming from PermissionId/RoleId to Permission/Role accurately reflects that these fields now accept either IDs or slugs/names. The comprehensive deletion warnings in the documentation are appropriate for these irreversible operations.

Also applies to: 1513-1527, 1540-1542, 1560-1566


776-776: Clean response type definitions

The response data types are now consistently defined as arrays of Permission or Role objects, providing a cleaner and more predictable API surface for clients.

Also applies to: 826-826, 1026-1026, 1078-1078, 1129-1129, 1182-1182

go/apps/api/openapi/openapi-generated.yaml (2)

1451-1455: Role-delete regex inconsistent with role-add (dot missing).

role deletion pattern omits the dot (.) allowed during add/set operations (^[a-zA-Z][a-zA-Z0-9._-]*$). Users could add finance.read but never delete it.

-                    pattern: "^[a-zA-Z][a-zA-Z0-9_-]*$"
+                    pattern: "^[a-zA-Z][a-zA-Z0-9._-]*$"
⛔ Skipped due to 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: ogzhanolguncu
PR: unkeyed/unkey#3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses `roleId` as the property name for the role identifier, not `id`. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.
Learnt from: chronark
PR: unkeyed/unkey#3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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.

1513-1518: Same dot-exclusion issue for role-get endpoint.

Apply the same regex fix as the delete-role block to maintain consistency.

⛔ Skipped due to 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: AkshayBandi027
PR: unkeyed/unkey#2215
File: apps/dashboard/app/(app)/@breadcrumb/authorization/roles/[roleId]/page.tsx:28-29
Timestamp: 2024-10-08T15:33:04.290Z
Learning: In `authorization/roles/[roleId]/update-role.tsx`, the tag `role-${role.id}` is revalidated after updating a role to ensure that the caching mechanism is properly handled for roles.
Learnt from: ogzhanolguncu
PR: unkeyed/unkey#3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses `roleId` as the property name for the role identifier, not `id`. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

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: 24

🔭 Outside diff range comments (1)
go/apps/api/routes/v2_keys_set_roles/200_test.go (1)

35-35: Add missing RBAC permissions for set‐roles tests

The 200 test in go/apps/api/routes/v2_keys_set_roles/200_test.go only gives the root key the "api.*.update_key" permission, but setting roles internally performs both add and remove operations. As in the add_roles and remove_roles tests, you need to grant both RBAC permissions:

• File: go/apps/api/routes/v2_keys_set_roles/200_test.go
Line 35

- rootKey := h.CreateRootKey(workspace.ID, "api.*.update_key")
+ rootKey := h.CreateRootKey(
+   workspace.ID,
+   "api.*.update_key",
+   "rbac.*.add_role_to_key",
+   "rbac.*.remove_role_from_key",
+ )

Apply the same update to the other v2_keys_set_roles/*_test.go files (400, 404, etc.) to keep them consistent.

♻️ Duplicate comments (12)
go/apps/api/routes/v2_keys_update_key/handler.go (1)

92-92: Address the TODO comment about role permission checks.

This TODO was already flagged in a previous review. The missing RBAC checks for role operations should be implemented.

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

202-202: Ensure specific validation error messages are retained.

The test now only asserts a generic "failed to validate schema" error message, which reduces clarity on specific validation issues. This aligns with the concern raised in previous reviews about maintaining comprehensive error feedback for users.

go/pkg/db/role_find_many_by_id_or_name_with_perms.sql_generated.go (1)

49-49: Use []byte instead of interface{} for the Permissions field.

Same issue as in other generated files. The Permissions field should be []byte for proper JSON handling.

go/apps/api/openapi/openapi-generated.yaml (9)

968-983: Same slug-only wording + pattern duplication – see earlier comments.


1201-1207: Inconsistent wording again (“slug” only)
Please apply the same wording fix here to stay consistent.


2106-2110: List description should match mixed identifier capability
Same wording fix as earlier: slugs or IDs.


2508-2511: Consistent wording needed – same slug vs slug/ID issue.


533-545: Still says “slug” even though the endpoint now accepts slug or ID
This is the exact wording flagged in a previous round and is unchanged.

-                        description: Specify the permission by its slug.
+                        description: Specify the permission by its slug **or** its `perm_*` ID.

876-895: Same “slug only” wording issue – please keep the docs consistent with the new mixed identifier capability.


927-938: Role removal description ignores IDs
Replace “role by name” with “role by name or role_* ID”.


955-963: keyId pattern still too permissive – allows fooBar
Previous review asked to enforce the required key_ prefix; pattern is unchanged.

-                    pattern: "^[a-zA-Z0-9_]+$"
+                    pattern: "^key_[a-zA-Z0-9]+$"

2352-2361: Permission.id regex should enforce perm_ prefix

-                    pattern: "^[a-zA-Z0-9_]+$"
+                    pattern: "^perm_[a-zA-Z0-9]+$"

Without this, a plain string like foo passes validation despite docs stating it “Always begins with 'perm_'”.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 8c76365 and 36eb06f.

📒 Files selected for processing (87)
  • go/apps/api/openapi/gen.go (19 hunks)
  • go/apps/api/openapi/openapi-generated.yaml (22 hunks)
  • go/apps/api/openapi/spec/common/role.yaml (0 hunks)
  • go/apps/api/openapi/spec/paths/v2/keys/addRoles/V2KeysAddRolesRequestBody.yaml (1 hunks)
  • go/apps/api/openapi/spec/paths/v2/keys/addRoles/V2KeysAddRolesResponseData.yaml (1 hunks)
  • go/apps/api/openapi/spec/paths/v2/keys/removeRoles/V2KeysRemoveRolesRequestBody.yaml (1 hunks)
  • go/apps/api/openapi/spec/paths/v2/keys/removeRoles/V2KeysRemoveRolesResponseData.yaml (1 hunks)
  • go/apps/api/openapi/spec/paths/v2/keys/setRoles/V2KeysSetRolesRequestBody.yaml (1 hunks)
  • go/apps/api/openapi/spec/paths/v2/keys/setRoles/V2KeysSetRolesResponseData.yaml (1 hunks)
  • go/apps/api/openapi/spec/paths/v2/permissions/deleteRole/V2PermissionsDeleteRoleRequestBody.yaml (1 hunks)
  • go/apps/api/openapi/spec/paths/v2/permissions/getRole/V2PermissionsGetRoleRequestBody.yaml (1 hunks)
  • go/apps/api/routes/v2_apis_list_keys/handler.go (1 hunks)
  • go/apps/api/routes/v2_keys_add_permissions/400_test.go (6 hunks)
  • go/apps/api/routes/v2_keys_add_permissions/401_test.go (2 hunks)
  • go/apps/api/routes/v2_keys_add_permissions/403_test.go (3 hunks)
  • go/apps/api/routes/v2_keys_add_permissions/404_test.go (4 hunks)
  • go/apps/api/routes/v2_keys_add_permissions/handler.go (6 hunks)
  • go/apps/api/routes/v2_keys_add_roles/200_test.go (4 hunks)
  • go/apps/api/routes/v2_keys_add_roles/400_test.go (3 hunks)
  • go/apps/api/routes/v2_keys_add_roles/401_test.go (1 hunks)
  • go/apps/api/routes/v2_keys_add_roles/403_test.go (4 hunks)
  • go/apps/api/routes/v2_keys_add_roles/404_test.go (11 hunks)
  • go/apps/api/routes/v2_keys_add_roles/handler.go (8 hunks)
  • go/apps/api/routes/v2_keys_create_key/handler.go (6 hunks)
  • go/apps/api/routes/v2_keys_remove_permissions/200_test.go (10 hunks)
  • go/apps/api/routes/v2_keys_remove_permissions/400_test.go (2 hunks)
  • go/apps/api/routes/v2_keys_remove_permissions/404_test.go (7 hunks)
  • go/apps/api/routes/v2_keys_remove_roles/200_test.go (4 hunks)
  • go/apps/api/routes/v2_keys_remove_roles/400_test.go (9 hunks)
  • go/apps/api/routes/v2_keys_remove_roles/401_test.go (6 hunks)
  • go/apps/api/routes/v2_keys_remove_roles/403_test.go (7 hunks)
  • go/apps/api/routes/v2_keys_remove_roles/404_test.go (8 hunks)
  • go/apps/api/routes/v2_keys_remove_roles/handler.go (8 hunks)
  • go/apps/api/routes/v2_keys_set_permissions/200_test.go (14 hunks)
  • go/apps/api/routes/v2_keys_set_permissions/401_test.go (2 hunks)
  • go/apps/api/routes/v2_keys_set_permissions/403_test.go (2 hunks)
  • go/apps/api/routes/v2_keys_set_permissions/404_test.go (4 hunks)
  • go/apps/api/routes/v2_keys_set_permissions/handler.go (6 hunks)
  • go/apps/api/routes/v2_keys_set_roles/200_test.go (7 hunks)
  • go/apps/api/routes/v2_keys_set_roles/400_test.go (0 hunks)
  • go/apps/api/routes/v2_keys_set_roles/401_test.go (1 hunks)
  • go/apps/api/routes/v2_keys_set_roles/403_test.go (2 hunks)
  • go/apps/api/routes/v2_keys_set_roles/404_test.go (14 hunks)
  • go/apps/api/routes/v2_keys_set_roles/handler.go (5 hunks)
  • go/apps/api/routes/v2_keys_update_key/handler.go (8 hunks)
  • go/apps/api/routes/v2_permissions_create_permission/handler.go (4 hunks)
  • go/apps/api/routes/v2_permissions_create_role/handler.go (4 hunks)
  • go/apps/api/routes/v2_permissions_delete_permission/200_test.go (5 hunks)
  • go/apps/api/routes/v2_permissions_delete_permission/403_test.go (4 hunks)
  • go/apps/api/routes/v2_permissions_delete_permission/404_test.go (5 hunks)
  • go/apps/api/routes/v2_permissions_delete_role/200_test.go (4 hunks)
  • go/apps/api/routes/v2_permissions_delete_role/400_test.go (2 hunks)
  • go/apps/api/routes/v2_permissions_delete_role/401_test.go (1 hunks)
  • go/apps/api/routes/v2_permissions_delete_role/403_test.go (2 hunks)
  • go/apps/api/routes/v2_permissions_delete_role/404_test.go (2 hunks)
  • go/apps/api/routes/v2_permissions_delete_role/handler.go (2 hunks)
  • go/apps/api/routes/v2_permissions_get_permission/200_test.go (4 hunks)
  • go/apps/api/routes/v2_permissions_get_permission/403_test.go (4 hunks)
  • go/apps/api/routes/v2_permissions_get_role/200_test.go (6 hunks)
  • go/apps/api/routes/v2_permissions_get_role/400_test.go (1 hunks)
  • go/apps/api/routes/v2_permissions_get_role/401_test.go (1 hunks)
  • go/apps/api/routes/v2_permissions_get_role/403_test.go (2 hunks)
  • go/apps/api/routes/v2_permissions_get_role/404_test.go (1 hunks)
  • go/apps/api/routes/v2_permissions_get_role/handler.go (3 hunks)
  • go/apps/api/routes/v2_permissions_list_permissions/200_test.go (4 hunks)
  • go/apps/api/routes/v2_permissions_list_permissions/403_test.go (3 hunks)
  • go/apps/api/routes/v2_permissions_list_roles/200_test.go (2 hunks)
  • go/apps/api/routes/v2_permissions_list_roles/handler.go (2 hunks)
  • go/pkg/db/key_role_delete_many_by_key_and_role_ids.sql_generated.go (1 hunks)
  • go/pkg/db/models_generated.go (2 hunks)
  • go/pkg/db/permission_insert.sql_generated.go (2 hunks)
  • go/pkg/db/plugins/bulk-insert/template_test.go (4 hunks)
  • go/pkg/db/querier_generated.go (7 hunks)
  • go/pkg/db/queries/key_role_delete_many_by_key_and_role_ids.sql (1 hunks)
  • go/pkg/db/queries/role_find_by_id_or_name_with_perms.sql (1 hunks)
  • go/pkg/db/queries/role_find_many_by_id_or_name_with_perms.sql (1 hunks)
  • go/pkg/db/queries/role_find_many_by_name_with_perms.sql (1 hunks)
  • go/pkg/db/queries/role_list.sql (1 hunks)
  • go/pkg/db/queries/role_list_by_key_id.sql (1 hunks)
  • go/pkg/db/role_find_by_id_or_name_with_perms.sql_generated.go (1 hunks)
  • go/pkg/db/role_find_many_by_id_or_name_with_perms.sql_generated.go (1 hunks)
  • go/pkg/db/role_find_many_by_name_with_perms.sql_generated.go (1 hunks)
  • go/pkg/db/role_list.sql_generated.go (2 hunks)
  • go/pkg/db/role_list_by_key_id.sql_generated.go (1 hunks)
  • go/pkg/db/sqlc.json (1 hunks)
  • go/pkg/db/types/null_string.go (1 hunks)
  • go/pkg/testutil/seed/seed.go (3 hunks)
💤 Files with no reviewable changes (2)
  • go/apps/api/routes/v2_keys_set_roles/400_test.go
  • go/apps/api/openapi/spec/common/role.yaml
🧰 Additional context used
🧠 Learnings (81)
📓 Common learnings
Learnt from: ogzhanolguncu
PR: unkeyed/unkey#3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses `roleId` as the property name for the role identifier, not `id`. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.
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/apps/api/routes/v2_permissions_get_permission/403_test.go (3)

Learnt from: Flo4604
PR: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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.

Learnt from: chronark
PR: #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: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

go/apps/api/openapi/spec/paths/v2/permissions/getRole/V2PermissionsGetRoleRequestBody.yaml (3)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

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.

Learnt from: AkshayBandi027
PR: #2215
File: apps/dashboard/app/(app)/@breadcrumb/authorization/roles/[roleId]/page.tsx:28-29
Timestamp: 2024-10-08T15:33:04.290Z
Learning: In authorization/roles/[roleId]/update-role.tsx, the tag role-${role.id} is revalidated after updating a role to ensure that the caching mechanism is properly handled for roles.

go/apps/api/routes/v2_keys_remove_permissions/400_test.go (7)

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.

Learnt from: MichaelUnkey
PR: #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: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: #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: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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.

Learnt from: chronark
PR: #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: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

go/apps/api/routes/v2_keys_add_permissions/403_test.go (7)

Learnt from: MichaelUnkey
PR: #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: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

Learnt from: Flo4604
PR: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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.

Learnt from: chronark
PR: #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: 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.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use ctx.workspace.id directly instead of fetching the workspace separately for better performance and security.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: When querying or updating namespaces in the Unkey dashboard, always scope the operations to the current workspace using eq(table.workspaceId, ctx.workspace.id) to prevent cross-workspace access.

go/pkg/db/key_role_delete_many_by_key_and_role_ids.sql_generated.go (1)

Learnt from: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

go/apps/api/routes/v2_permissions_delete_permission/404_test.go (4)

Learnt from: ogzhanolguncu
PR: #3661
File: go/apps/api/routes/v2_identities_update_identity/handler.go:115-119
Timestamp: 2025-07-28T11:47:43.144Z
Learning: The v2 update identity endpoint (go/apps/api/routes/v2_identities_update_identity/handler.go) intentionally uses ExternalId field instead of the unified Identity field used in other v2 identity endpoints. This is because the update endpoint needs to both find by externalId and potentially update the externalId value, making the specific field name more appropriate than the generic Identity field.

Learnt from: Flo4604
PR: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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.

Learnt from: chronark
PR: #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: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

go/pkg/db/models_generated.go (2)

Learnt from: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

Learnt from: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

go/apps/api/openapi/spec/paths/v2/keys/removeRoles/V2KeysRemoveRolesResponseData.yaml (5)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

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.

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.

Learnt from: chronark
PR: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

Learnt from: AkshayBandi027
PR: #2215
File: apps/dashboard/app/(app)/@breadcrumb/authorization/roles/[roleId]/page.tsx:28-29
Timestamp: 2024-10-08T15:33:04.290Z
Learning: In authorization/roles/[roleId]/update-role.tsx, the tag role-${role.id} is revalidated after updating a role to ensure that the caching mechanism is properly handled for roles.

go/apps/api/routes/v2_keys_set_permissions/404_test.go (10)

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: #2126
File: apps/api/src/routes/v1_ratelimit_getOverride.happy.test.ts:36-36
Timestamp: 2024-11-13T19:06:36.786Z
Learning: In the rate limit test files (e.g., apps/api/src/routes/v1_ratelimit_getOverride.happy.test.ts), URL parameters like namespaceId and identifier do not need to be URL-encoded in the test code because the values used are always considered safe within the test environment.

Learnt from: chronark
PR: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

Learnt from: Flo4604
PR: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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.

Learnt from: chronark
PR: #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: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: When querying or updating namespaces in the Unkey dashboard, always scope the operations to the current workspace using eq(table.workspaceId, ctx.workspace.id) to prevent cross-workspace access.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use ctx.workspace.id directly instead of fetching the workspace separately for better performance and security.

Learnt from: MichaelUnkey
PR: #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: #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/apps/api/routes/v2_permissions_delete_role/401_test.go (4)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

Learnt from: AkshayBandi027
PR: #2215
File: apps/dashboard/app/(app)/@breadcrumb/authorization/roles/[roleId]/page.tsx:28-29
Timestamp: 2024-10-08T15:33:04.290Z
Learning: In authorization/roles/[roleId]/update-role.tsx, the tag role-${role.id} is revalidated after updating a role to ensure that the caching mechanism is properly handled for roles.

Learnt from: ogzhanolguncu
PR: #3661
File: go/apps/api/routes/v2_identities_update_identity/handler.go:115-119
Timestamp: 2025-07-28T11:47:43.144Z
Learning: The v2 update identity endpoint (go/apps/api/routes/v2_identities_update_identity/handler.go) intentionally uses ExternalId field instead of the unified Identity field used in other v2 identity endpoints. This is because the update endpoint needs to both find by externalId and potentially update the externalId value, making the specific field name more appropriate than the generic Identity field.

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.

go/apps/api/routes/v2_keys_remove_roles/400_test.go (7)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

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.

Learnt from: chronark
PR: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

Learnt from: ogzhanolguncu
PR: #3661
File: go/apps/api/routes/v2_identities_update_identity/handler.go:115-119
Timestamp: 2025-07-28T11:47:43.144Z
Learning: The v2 update identity endpoint (go/apps/api/routes/v2_identities_update_identity/handler.go) intentionally uses ExternalId field instead of the unified Identity field used in other v2 identity endpoints. This is because the update endpoint needs to both find by externalId and potentially update the externalId value, making the specific field name more appropriate than the generic Identity field.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: When querying or updating namespaces in the Unkey dashboard, always scope the operations to the current workspace using eq(table.workspaceId, ctx.workspace.id) to prevent cross-workspace access.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use ctx.workspace.id directly instead of fetching the workspace separately for better performance and security.

Learnt from: MichaelUnkey
PR: #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.

go/apps/api/routes/v2_keys_add_permissions/401_test.go (7)

Learnt from: MichaelUnkey
PR: #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: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

Learnt from: Flo4604
PR: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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.

Learnt from: chronark
PR: #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: 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.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use ctx.workspace.id directly instead of fetching the workspace separately for better performance and security.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: When querying or updating namespaces in the Unkey dashboard, always scope the operations to the current workspace using eq(table.workspaceId, ctx.workspace.id) to prevent cross-workspace access.

go/apps/api/openapi/spec/paths/v2/keys/addRoles/V2KeysAddRolesRequestBody.yaml (5)

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.

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

Learnt from: chronark
PR: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

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.

Learnt from: AkshayBandi027
PR: #2215
File: apps/dashboard/app/(app)/@breadcrumb/authorization/roles/[roleId]/page.tsx:28-29
Timestamp: 2024-10-08T15:33:04.290Z
Learning: In authorization/roles/[roleId]/update-role.tsx, the tag role-${role.id} is revalidated after updating a role to ensure that the caching mechanism is properly handled for roles.

go/apps/api/routes/v2_permissions_delete_role/403_test.go (4)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

Learnt from: ogzhanolguncu
PR: #3661
File: go/apps/api/routes/v2_identities_update_identity/handler.go:115-119
Timestamp: 2025-07-28T11:47:43.144Z
Learning: The v2 update identity endpoint (go/apps/api/routes/v2_identities_update_identity/handler.go) intentionally uses ExternalId field instead of the unified Identity field used in other v2 identity endpoints. This is because the update endpoint needs to both find by externalId and potentially update the externalId value, making the specific field name more appropriate than the generic Identity field.

Learnt from: AkshayBandi027
PR: #2215
File: apps/dashboard/app/(app)/@breadcrumb/authorization/roles/[roleId]/page.tsx:28-29
Timestamp: 2024-10-08T15:33:04.290Z
Learning: In authorization/roles/[roleId]/update-role.tsx, the tag role-${role.id} is revalidated after updating a role to ensure that the caching mechanism is properly handled for roles.

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.

go/apps/api/routes/v2_keys_add_roles/404_test.go (6)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

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.

Learnt from: ogzhanolguncu
PR: #3661
File: go/apps/api/routes/v2_identities_update_identity/handler.go:115-119
Timestamp: 2025-07-28T11:47:43.144Z
Learning: The v2 update identity endpoint (go/apps/api/routes/v2_identities_update_identity/handler.go) intentionally uses ExternalId field instead of the unified Identity field used in other v2 identity endpoints. This is because the update endpoint needs to both find by externalId and potentially update the externalId value, making the specific field name more appropriate than the generic Identity field.

Learnt from: MichaelUnkey
PR: #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: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: When querying or updating namespaces in the Unkey dashboard, always scope the operations to the current workspace using eq(table.workspaceId, ctx.workspace.id) to prevent cross-workspace access.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use ctx.workspace.id directly instead of fetching the workspace separately for better performance and security.

go/pkg/db/role_find_many_by_id_or_name_with_perms.sql_generated.go (1)

Learnt from: chronark
PR: #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_set_roles/403_test.go (3)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

Learnt from: ogzhanolguncu
PR: #3661
File: go/apps/api/routes/v2_identities_update_identity/handler.go:115-119
Timestamp: 2025-07-28T11:47:43.144Z
Learning: The v2 update identity endpoint (go/apps/api/routes/v2_identities_update_identity/handler.go) intentionally uses ExternalId field instead of the unified Identity field used in other v2 identity endpoints. This is because the update endpoint needs to both find by externalId and potentially update the externalId value, making the specific field name more appropriate than the generic Identity field.

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.

go/pkg/db/role_find_many_by_name_with_perms.sql_generated.go (1)

Learnt from: chronark
PR: #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_set_permissions/401_test.go (3)

Learnt from: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

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.

Learnt from: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

go/apps/api/routes/v2_permissions_delete_role/404_test.go (3)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

Learnt from: AkshayBandi027
PR: #2215
File: apps/dashboard/app/(app)/@breadcrumb/authorization/roles/[roleId]/page.tsx:28-29
Timestamp: 2024-10-08T15:33:04.290Z
Learning: In authorization/roles/[roleId]/update-role.tsx, the tag role-${role.id} is revalidated after updating a role to ensure that the caching mechanism is properly handled for roles.

Learnt from: ogzhanolguncu
PR: #3661
File: go/apps/api/routes/v2_identities_update_identity/handler.go:115-119
Timestamp: 2025-07-28T11:47:43.144Z
Learning: The v2 update identity endpoint (go/apps/api/routes/v2_identities_update_identity/handler.go) intentionally uses ExternalId field instead of the unified Identity field used in other v2 identity endpoints. This is because the update endpoint needs to both find by externalId and potentially update the externalId value, making the specific field name more appropriate than the generic Identity field.

go/apps/api/routes/v2_keys_remove_roles/403_test.go (2)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

Learnt from: ogzhanolguncu
PR: #3661
File: go/apps/api/routes/v2_identities_update_identity/handler.go:115-119
Timestamp: 2025-07-28T11:47:43.144Z
Learning: The v2 update identity endpoint (go/apps/api/routes/v2_identities_update_identity/handler.go) intentionally uses ExternalId field instead of the unified Identity field used in other v2 identity endpoints. This is because the update endpoint needs to both find by externalId and potentially update the externalId value, making the specific field name more appropriate than the generic Identity field.

go/pkg/db/queries/key_role_delete_many_by_key_and_role_ids.sql (1)

Learnt from: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

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

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.

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

Learnt from: MichaelUnkey
PR: #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.

go/apps/api/routes/v2_permissions_delete_role/handler.go (4)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

Learnt from: ogzhanolguncu
PR: #3661
File: go/apps/api/routes/v2_identities_update_identity/handler.go:115-119
Timestamp: 2025-07-28T11:47:43.144Z
Learning: The v2 update identity endpoint (go/apps/api/routes/v2_identities_update_identity/handler.go) intentionally uses ExternalId field instead of the unified Identity field used in other v2 identity endpoints. This is because the update endpoint needs to both find by externalId and potentially update the externalId value, making the specific field name more appropriate than the generic Identity field.

Learnt from: AkshayBandi027
PR: #2215
File: apps/dashboard/app/(app)/@breadcrumb/authorization/roles/[roleId]/page.tsx:28-29
Timestamp: 2024-10-08T15:33:04.290Z
Learning: In authorization/roles/[roleId]/update-role.tsx, the tag role-${role.id} is revalidated after updating a role to ensure that the caching mechanism is properly handled for roles.

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.

go/pkg/db/permission_insert.sql_generated.go (4)

Learnt from: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

Learnt from: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

Learnt from: chronark
PR: #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: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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/apps/api/routes/v2_keys_add_roles/403_test.go (2)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

Learnt from: ogzhanolguncu
PR: #3661
File: go/apps/api/routes/v2_identities_update_identity/handler.go:115-119
Timestamp: 2025-07-28T11:47:43.144Z
Learning: The v2 update identity endpoint (go/apps/api/routes/v2_identities_update_identity/handler.go) intentionally uses ExternalId field instead of the unified Identity field used in other v2 identity endpoints. This is because the update endpoint needs to both find by externalId and potentially update the externalId value, making the specific field name more appropriate than the generic Identity field.

go/apps/api/routes/v2_permissions_delete_permission/403_test.go (4)

Learnt from: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

Learnt from: Flo4604
PR: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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.

Learnt from: chronark
PR: #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: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

go/apps/api/openapi/spec/paths/v2/keys/setRoles/V2KeysSetRolesResponseData.yaml (3)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

Learnt from: AkshayBandi027
PR: #2215
File: apps/dashboard/app/(app)/@breadcrumb/authorization/roles/[roleId]/page.tsx:28-29
Timestamp: 2024-10-08T15:33:04.290Z
Learning: In authorization/roles/[roleId]/update-role.tsx, the tag role-${role.id} is revalidated after updating a role to ensure that the caching mechanism is properly handled for roles.

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.

go/apps/api/routes/v2_keys_remove_permissions/200_test.go (7)

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: #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: #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: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

Learnt from: Flo4604
PR: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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.

Learnt from: chronark
PR: #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: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

go/apps/api/routes/v2_keys_remove_permissions/404_test.go (6)

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: #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: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: When querying or updating namespaces in the Unkey dashboard, always scope the operations to the current workspace using eq(table.workspaceId, ctx.workspace.id) to prevent cross-workspace access.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use ctx.workspace.id directly instead of fetching the workspace separately for better performance and security.

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.

go/pkg/db/plugins/bulk-insert/template_test.go (4)

Learnt from: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

Learnt from: Flo4604
PR: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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.

Learnt from: chronark
PR: #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: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

go/apps/api/routes/v2_permissions_delete_role/400_test.go (3)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

Learnt from: AkshayBandi027
PR: #2215
File: apps/dashboard/app/(app)/@breadcrumb/authorization/roles/[roleId]/page.tsx:28-29
Timestamp: 2024-10-08T15:33:04.290Z
Learning: In authorization/roles/[roleId]/update-role.tsx, the tag role-${role.id} is revalidated after updating a role to ensure that the caching mechanism is properly handled for roles.

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.

go/apps/api/routes/v2_keys_set_permissions/handler.go (6)

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.

Learnt from: ogzhanolguncu
PR: #3661
File: go/apps/api/routes/v2_identities_update_identity/handler.go:115-119
Timestamp: 2025-07-28T11:47:43.144Z
Learning: The v2 update identity endpoint (go/apps/api/routes/v2_identities_update_identity/handler.go) intentionally uses ExternalId field instead of the unified Identity field used in other v2 identity endpoints. This is because the update endpoint needs to both find by externalId and potentially update the externalId value, making the specific field name more appropriate than the generic Identity field.

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.

Learnt from: chronark
PR: #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: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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.

Learnt from: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

go/apps/api/routes/v2_keys_set_permissions/403_test.go (10)

Learnt from: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

Learnt from: MichaelUnkey
PR: #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: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: CR
PR: unkeyed/unkey#0
File: go/deploy/CLAUDE.md:0-0
Timestamp: 2025-07-21T18:05:58.236Z
Learning: Applies to go/deploy/**/*.{go,js,ts,tsx,py,sh,md,txt,json,yaml,yml,ini,env,conf,html,css,scss,xml,c,h,cpp,java,rb,rs,php,pl,sql} : 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.

Learnt from: CR
PR: unkeyed/unkey#0
File: go/deploy/CLAUDE.md:0-0
Timestamp: 2025-07-21T18:05:58.236Z
Learning: Applies to go/deploy/**/*.{go,js,ts,tsx,py,sh,md,txt,json,yaml,yml,ini,env,conf,html,css,scss,xml,c,h,cpp,java,rb,rs,php,pl,sql} : Do not remove AIDEV-*s without explicit human instruction.

Learnt from: Flo4604
PR: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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.

Learnt from: chronark
PR: #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: 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.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use ctx.workspace.id directly instead of fetching the workspace separately for better performance and security.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: When querying or updating namespaces in the Unkey dashboard, always scope the operations to the current workspace using eq(table.workspaceId, ctx.workspace.id) to prevent cross-workspace access.

go/apps/api/openapi/spec/paths/v2/permissions/deleteRole/V2PermissionsDeleteRoleRequestBody.yaml (3)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

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.

Learnt from: AkshayBandi027
PR: #2215
File: apps/dashboard/app/(app)/@breadcrumb/authorization/roles/[roleId]/page.tsx:28-29
Timestamp: 2024-10-08T15:33:04.290Z
Learning: In authorization/roles/[roleId]/update-role.tsx, the tag role-${role.id} is revalidated after updating a role to ensure that the caching mechanism is properly handled for roles.

go/apps/api/routes/v2_permissions_get_role/404_test.go (2)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

Learnt from: AkshayBandi027
PR: #2215
File: apps/dashboard/app/(app)/@breadcrumb/authorization/roles/[roleId]/page.tsx:28-29
Timestamp: 2024-10-08T15:33:04.290Z
Learning: In authorization/roles/[roleId]/update-role.tsx, the tag role-${role.id} is revalidated after updating a role to ensure that the caching mechanism is properly handled for roles.

go/apps/api/routes/v2_keys_remove_roles/401_test.go (6)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

Learnt from: ogzhanolguncu
PR: #3661
File: go/apps/api/routes/v2_identities_update_identity/handler.go:115-119
Timestamp: 2025-07-28T11:47:43.144Z
Learning: The v2 update identity endpoint (go/apps/api/routes/v2_identities_update_identity/handler.go) intentionally uses ExternalId field instead of the unified Identity field used in other v2 identity endpoints. This is because the update endpoint needs to both find by externalId and potentially update the externalId value, making the specific field name more appropriate than the generic Identity field.

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.

Learnt from: AkshayBandi027
PR: #2215
File: apps/dashboard/app/(app)/@breadcrumb/authorization/roles/[roleId]/page.tsx:28-29
Timestamp: 2024-10-08T15:33:04.290Z
Learning: In authorization/roles/[roleId]/update-role.tsx, the tag role-${role.id} is revalidated after updating a role to ensure that the caching mechanism is properly handled for roles.

Learnt from: chronark
PR: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use ctx.workspace.id directly instead of fetching the workspace separately for better performance and security.

go/apps/api/openapi/spec/paths/v2/keys/setRoles/V2KeysSetRolesRequestBody.yaml (5)

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.

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

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.

Learnt from: chronark
PR: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

Learnt from: AkshayBandi027
PR: #2215
File: apps/dashboard/app/(app)/@breadcrumb/authorization/roles/[roleId]/page.tsx:28-29
Timestamp: 2024-10-08T15:33:04.290Z
Learning: In authorization/roles/[roleId]/update-role.tsx, the tag role-${role.id} is revalidated after updating a role to ensure that the caching mechanism is properly handled for roles.

go/apps/api/routes/v2_permissions_create_permission/handler.go (4)

Learnt from: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

Learnt from: chronark
PR: #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: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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.

Learnt from: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

go/apps/api/routes/v2_permissions_get_role/401_test.go (4)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

Learnt from: AkshayBandi027
PR: #2215
File: apps/dashboard/app/(app)/@breadcrumb/authorization/roles/[roleId]/page.tsx:28-29
Timestamp: 2024-10-08T15:33:04.290Z
Learning: In authorization/roles/[roleId]/update-role.tsx, the tag role-${role.id} is revalidated after updating a role to ensure that the caching mechanism is properly handled for roles.

Learnt from: ogzhanolguncu
PR: #3661
File: go/apps/api/routes/v2_identities_update_identity/handler.go:115-119
Timestamp: 2025-07-28T11:47:43.144Z
Learning: The v2 update identity endpoint (go/apps/api/routes/v2_identities_update_identity/handler.go) intentionally uses ExternalId field instead of the unified Identity field used in other v2 identity endpoints. This is because the update endpoint needs to both find by externalId and potentially update the externalId value, making the specific field name more appropriate than the generic Identity field.

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.

go/apps/api/routes/v2_permissions_create_role/handler.go (5)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

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.

Learnt from: Flo4604
PR: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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.

Learnt from: chronark
PR: #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: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

go/apps/api/routes/v2_apis_list_keys/handler.go (4)

Learnt from: ogzhanolguncu
PR: #3661
File: go/apps/api/routes/v2_identities_update_identity/handler.go:115-119
Timestamp: 2025-07-28T11:47:43.144Z
Learning: The v2 update identity endpoint (go/apps/api/routes/v2_identities_update_identity/handler.go) intentionally uses ExternalId field instead of the unified Identity field used in other v2 identity endpoints. This is because the update endpoint needs to both find by externalId and potentially update the externalId value, making the specific field name more appropriate than the generic Identity field.

Learnt from: chronark
PR: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

Learnt from: Flo4604
PR: #3151
File: go/apps/api/openapi/gen.go:221-233
Timestamp: 2025-04-18T20:01:33.812Z
Learning: For identity deletion operations in the Unkey API, identityId takes precedence over externalId when both are provided in the request body.

Learnt from: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

go/apps/api/routes/v2_permissions_get_role/handler.go (2)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

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.

go/pkg/db/queries/role_list.sql (1)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

go/apps/api/routes/v2_keys_add_roles/401_test.go (3)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

Learnt from: ogzhanolguncu
PR: #3661
File: go/apps/api/routes/v2_identities_update_identity/handler.go:115-119
Timestamp: 2025-07-28T11:47:43.144Z
Learning: The v2 update identity endpoint (go/apps/api/routes/v2_identities_update_identity/handler.go) intentionally uses ExternalId field instead of the unified Identity field used in other v2 identity endpoints. This is because the update endpoint needs to both find by externalId and potentially update the externalId value, making the specific field name more appropriate than the generic Identity field.

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.

go/pkg/db/queries/role_list_by_key_id.sql (3)

Learnt from: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

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.

go/apps/api/routes/v2_permissions_get_role/400_test.go (4)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

Learnt from: AkshayBandi027
PR: #2215
File: apps/dashboard/app/(app)/@breadcrumb/authorization/roles/[roleId]/page.tsx:28-29
Timestamp: 2024-10-08T15:33:04.290Z
Learning: In authorization/roles/[roleId]/update-role.tsx, the tag role-${role.id} is revalidated after updating a role to ensure that the caching mechanism is properly handled for roles.

Learnt from: ogzhanolguncu
PR: #3661
File: go/apps/api/routes/v2_identities_update_identity/handler.go:115-119
Timestamp: 2025-07-28T11:47:43.144Z
Learning: The v2 update identity endpoint (go/apps/api/routes/v2_identities_update_identity/handler.go) intentionally uses ExternalId field instead of the unified Identity field used in other v2 identity endpoints. This is because the update endpoint needs to both find by externalId and potentially update the externalId value, making the specific field name more appropriate than the generic Identity field.

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.

go/apps/api/routes/v2_keys_add_permissions/400_test.go (10)

Learnt from: MichaelUnkey
PR: #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: #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: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: #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: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

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.

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.

Learnt from: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: When querying or updating namespaces in the Unkey dashboard, always scope the operations to the current workspace using eq(table.workspaceId, ctx.workspace.id) to prevent cross-workspace access.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use ctx.workspace.id directly instead of fetching the workspace separately for better performance and security.

go/apps/api/routes/v2_keys_add_roles/400_test.go (7)

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.

Learnt from: MichaelUnkey
PR: #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: #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: AkshayBandi027
PR: #2215
File: apps/dashboard/app/(app)/@breadcrumb/authorization/roles/[roleId]/page.tsx:28-29
Timestamp: 2024-10-08T15:33:04.290Z
Learning: In authorization/roles/[roleId]/update-role.tsx, the tag role-${role.id} is revalidated after updating a role to ensure that the caching mechanism is properly handled for roles.

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: When querying or updating namespaces in the Unkey dashboard, always scope the operations to the current workspace using eq(table.workspaceId, ctx.workspace.id) to prevent cross-workspace access.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use ctx.workspace.id directly instead of fetching the workspace separately for better performance and security.

go/apps/api/routes/v2_permissions_list_roles/200_test.go (2)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

Learnt from: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

go/apps/api/routes/v2_permissions_list_permissions/403_test.go (4)

Learnt from: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

Learnt from: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

Learnt from: chronark
PR: #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: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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/apps/api/routes/v2_keys_update_key/handler.go (12)

Learnt from: chronark
PR: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: #2146
File: apps/dashboard/lib/trpc/routers/api/setDefaultPrefix.ts:80-80
Timestamp: 2024-10-04T17:27:08.666Z
Learning: Ensure that audit log descriptions accurately reflect the action being performed, such as updating the defaultPrefix, and avoid incorrect references like 'name' when not applicable.

Learnt from: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

Learnt from: ogzhanolguncu
PR: #3661
File: go/apps/api/routes/v2_identities_update_identity/handler.go:115-119
Timestamp: 2025-07-28T11:47:43.144Z
Learning: The v2 update identity endpoint (go/apps/api/routes/v2_identities_update_identity/handler.go) intentionally uses ExternalId field instead of the unified Identity field used in other v2 identity endpoints. This is because the update endpoint needs to both find by externalId and potentially update the externalId value, making the specific field name more appropriate than the generic Identity field.

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.

Learnt from: AkshayBandi027
PR: #2215
File: apps/dashboard/app/(app)/@breadcrumb/authorization/roles/[roleId]/page.tsx:28-29
Timestamp: 2024-10-08T15:33:04.290Z
Learning: In authorization/roles/[roleId]/update-role.tsx, the tag role-${role.id} is revalidated after updating a role to ensure that the caching mechanism is properly handled for roles.

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.

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

Learnt from: ogzhanolguncu
PR: #2825
File: apps/dashboard/app/(app)/logs-v2/hooks/use-bookmarked-filters.ts:0-0
Timestamp: 2025-01-30T20:51:44.359Z
Learning: The user (ogzhanolguncu) prefers to handle refactoring suggestions in separate PRs to maintain focus in the current PR.

Learnt from: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

Learnt from: Flo4604
PR: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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/apps/api/routes/v2_permissions_delete_permission/200_test.go (3)

Learnt from: Flo4604
PR: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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.

Learnt from: chronark
PR: #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: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

go/apps/api/openapi/spec/paths/v2/keys/removeRoles/V2KeysRemoveRolesRequestBody.yaml (5)

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.

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

Learnt from: chronark
PR: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

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.

Learnt from: AkshayBandi027
PR: #2215
File: apps/dashboard/app/(app)/@breadcrumb/authorization/roles/[roleId]/page.tsx:28-29
Timestamp: 2024-10-08T15:33:04.290Z
Learning: In authorization/roles/[roleId]/update-role.tsx, the tag role-${role.id} is revalidated after updating a role to ensure that the caching mechanism is properly handled for roles.

go/apps/api/routes/v2_keys_create_key/handler.go (6)

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.

Learnt from: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

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.

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

Learnt from: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

go/pkg/db/sqlc.json (2)

Learnt from: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

Learnt from: chronark
PR: #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_remove_roles/handler.go (8)

Learnt from: chronark
PR: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

Learnt from: ogzhanolguncu
PR: #3661
File: go/apps/api/routes/v2_identities_update_identity/handler.go:115-119
Timestamp: 2025-07-28T11:47:43.144Z
Learning: The v2 update identity endpoint (go/apps/api/routes/v2_identities_update_identity/handler.go) intentionally uses ExternalId field instead of the unified Identity field used in other v2 identity endpoints. This is because the update endpoint needs to both find by externalId and potentially update the externalId value, making the specific field name more appropriate than the generic Identity field.

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.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use ctx.workspace.id directly instead of fetching the workspace separately for better performance and security.

Learnt from: Flo4604
PR: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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.

Learnt from: chronark
PR: #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: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: When querying or updating namespaces in the Unkey dashboard, always scope the operations to the current workspace using eq(table.workspaceId, ctx.workspace.id) to prevent cross-workspace access.

go/apps/api/routes/v2_permissions_list_roles/handler.go (2)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

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.

go/apps/api/routes/v2_keys_remove_roles/404_test.go (5)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

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.

Learnt from: chronark
PR: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: When querying or updating namespaces in the Unkey dashboard, always scope the operations to the current workspace using eq(table.workspaceId, ctx.workspace.id) to prevent cross-workspace access.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use ctx.workspace.id directly instead of fetching the workspace separately for better performance and security.

go/apps/api/routes/v2_keys_add_permissions/404_test.go (9)

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: #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: #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: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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.

Learnt from: chronark
PR: #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: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: When querying or updating namespaces in the Unkey dashboard, always scope the operations to the current workspace using eq(table.workspaceId, ctx.workspace.id) to prevent cross-workspace access.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use ctx.workspace.id directly instead of fetching the workspace separately for better performance and security.

Learnt from: MichaelUnkey
PR: #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.

go/apps/api/routes/v2_keys_set_permissions/200_test.go (10)

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.

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.

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: MichaelUnkey
PR: #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: ogzhanolguncu
PR: #3661
File: go/apps/api/routes/v2_identities_update_identity/handler.go:115-119
Timestamp: 2025-07-28T11:47:43.144Z
Learning: The v2 update identity endpoint (go/apps/api/routes/v2_identities_update_identity/handler.go) intentionally uses ExternalId field instead of the unified Identity field used in other v2 identity endpoints. This is because the update endpoint needs to both find by externalId and potentially update the externalId value, making the specific field name more appropriate than the generic Identity field.

Learnt from: Flo4604
PR: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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.

Learnt from: chronark
PR: #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: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: When querying or updating namespaces in the Unkey dashboard, always scope the operations to the current workspace using eq(table.workspaceId, ctx.workspace.id) to prevent cross-workspace access.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use ctx.workspace.id directly instead of fetching the workspace separately for better performance and security.

go/apps/api/routes/v2_keys_add_roles/handler.go (5)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

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.

Learnt from: ogzhanolguncu
PR: #3661
File: go/apps/api/routes/v2_identities_update_identity/handler.go:115-119
Timestamp: 2025-07-28T11:47:43.144Z
Learning: The v2 update identity endpoint (go/apps/api/routes/v2_identities_update_identity/handler.go) intentionally uses ExternalId field instead of the unified Identity field used in other v2 identity endpoints. This is because the update endpoint needs to both find by externalId and potentially update the externalId value, making the specific field name more appropriate than the generic Identity field.

Learnt from: Flo4604
PR: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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.

Learnt from: chronark
PR: #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_permissions_get_role/403_test.go (4)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

Learnt from: AkshayBandi027
PR: #2215
File: apps/dashboard/app/(app)/@breadcrumb/authorization/roles/[roleId]/page.tsx:28-29
Timestamp: 2024-10-08T15:33:04.290Z
Learning: In authorization/roles/[roleId]/update-role.tsx, the tag role-${role.id} is revalidated after updating a role to ensure that the caching mechanism is properly handled for roles.

Learnt from: ogzhanolguncu
PR: #3661
File: go/apps/api/routes/v2_identities_update_identity/handler.go:115-119
Timestamp: 2025-07-28T11:47:43.144Z
Learning: The v2 update identity endpoint (go/apps/api/routes/v2_identities_update_identity/handler.go) intentionally uses ExternalId field instead of the unified Identity field used in other v2 identity endpoints. This is because the update endpoint needs to both find by externalId and potentially update the externalId value, making the specific field name more appropriate than the generic Identity field.

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.

go/apps/api/routes/v2_keys_set_roles/401_test.go (4)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

Learnt from: ogzhanolguncu
PR: #3661
File: go/apps/api/routes/v2_identities_update_identity/handler.go:115-119
Timestamp: 2025-07-28T11:47:43.144Z
Learning: The v2 update identity endpoint (go/apps/api/routes/v2_identities_update_identity/handler.go) intentionally uses ExternalId field instead of the unified Identity field used in other v2 identity endpoints. This is because the update endpoint needs to both find by externalId and potentially update the externalId value, making the specific field name more appropriate than the generic Identity field.

Learnt from: AkshayBandi027
PR: #2215
File: apps/dashboard/app/(app)/@breadcrumb/authorization/roles/[roleId]/page.tsx:28-29
Timestamp: 2024-10-08T15:33:04.290Z
Learning: In authorization/roles/[roleId]/update-role.tsx, the tag role-${role.id} is revalidated after updating a role to ensure that the caching mechanism is properly handled for roles.

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.

go/pkg/testutil/seed/seed.go (3)

Learnt from: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

Learnt from: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

Learnt from: chronark
PR: #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_permissions_list_permissions/200_test.go (4)

Learnt from: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

Learnt from: Flo4604
PR: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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.

Learnt from: chronark
PR: #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: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

go/pkg/db/role_list_by_key_id.sql_generated.go (2)

Learnt from: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

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

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

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.

Learnt from: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

go/apps/api/routes/v2_keys_remove_roles/200_test.go (4)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

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.

Learnt from: chronark
PR: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use ctx.workspace.id directly instead of fetching the workspace separately for better performance and security.

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

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

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.

Learnt from: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

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

Learnt from: Flo4604
PR: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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.

Learnt from: chronark
PR: #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: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

go/apps/api/openapi/spec/paths/v2/keys/addRoles/V2KeysAddRolesResponseData.yaml (3)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

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.

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.

go/apps/api/routes/v2_keys_set_roles/404_test.go (2)

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

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.

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

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

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.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use ctx.workspace.id directly instead of fetching the workspace separately for better performance and security.

go/pkg/db/types/null_string.go (1)

Learnt from: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

go/apps/api/routes/v2_keys_add_permissions/handler.go (7)

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.

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.

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: Flo4604
PR: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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.

Learnt from: chronark
PR: #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: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

Learnt from: Flo4604
PR: #3606
File: go/pkg/prometheus/metrics/database.go:29-30
Timestamp: 2025-07-16T10:06:35.397Z
Learning: In Go packages, variables defined in one file within a package (like latencyBuckets and constLabels in go/pkg/prometheus/metrics/http.go) are accessible from other files in the same package without requiring imports. This is a common pattern for sharing configuration across multiple files within a package.

go/pkg/db/querier_generated.go (4)

Learnt from: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

Learnt from: chronark
PR: #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: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use ctx.workspace.id directly instead of fetching the workspace separately for better performance and security.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: When querying or updating namespaces in the Unkey dashboard, always scope the operations to the current workspace using eq(table.workspaceId, ctx.workspace.id) to prevent cross-workspace access.

go/apps/api/routes/v2_keys_set_roles/handler.go (7)

Learnt from: ogzhanolguncu
PR: #3661
File: go/apps/api/routes/v2_identities_update_identity/handler.go:115-119
Timestamp: 2025-07-28T11:47:43.144Z
Learning: The v2 update identity endpoint (go/apps/api/routes/v2_identities_update_identity/handler.go) intentionally uses ExternalId field instead of the unified Identity field used in other v2 identity endpoints. This is because the update endpoint needs to both find by externalId and potentially update the externalId value, making the specific field name more appropriate than the generic Identity field.

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

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.

Learnt from: chronark
PR: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

Learnt from: AkshayBandi027
PR: #2215
File: apps/dashboard/app/(app)/@breadcrumb/authorization/roles/[roleId]/page.tsx:28-29
Timestamp: 2024-10-08T15:33:04.290Z
Learning: In authorization/roles/[roleId]/update-role.tsx, the tag role-${role.id} is revalidated after updating a role to ensure that the caching mechanism is properly handled for roles.

Learnt from: Flo4604
PR: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
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.

Learnt from: chronark
PR: #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/openapi/openapi-generated.yaml (16)

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.

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: #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: #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: #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: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

Learnt from: CR
PR: unkeyed/unkey#0
File: go/deploy/CLAUDE.md:0-0
Timestamp: 2025-07-21T18:05:58.236Z
Learning: Applies to go/deploy/**/*.{go,js,ts,tsx,py,sh,md,txt,json,yaml,yml,ini,env,conf,html,css,scss,xml,c,h,cpp,java,rb,rs,php,pl,sql} : Do not remove AIDEV-*s without explicit human instruction.

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

Learnt from: CR
PR: unkeyed/unkey#0
File: go/deploy/CLAUDE.md:0-0
Timestamp: 2025-07-21T18:05:58.236Z
Learning: Applies to go/deploy/**/*.{go,js,ts,tsx,py,sh,md,txt,json,yaml,yml,ini,env,conf,html,css,scss,xml,c,h,cpp,java,rb,rs,php,pl,sql} : 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.

Learnt from: MichaelUnkey
PR: #3173
File: apps/docs/security/delete-protection.mdx:32-36
Timestamp: 2025-04-22T17:33:28.162Z
Learning: In the Unkey dashboard UI for delete protection, the button/link to initiate the process is labeled "Disable Delete Protection" while the confirmation button is labeled "Disable API Delete Protection". The documentation should maintain these different labels to match the actual UI.

Learnt from: MichaelUnkey
PR: #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: ogzhanolguncu
PR: #3661
File: go/apps/api/routes/v2_identities_update_identity/handler.go:115-119
Timestamp: 2025-07-28T11:47:43.144Z
Learning: The v2 update identity endpoint (go/apps/api/routes/v2_identities_update_identity/handler.go) intentionally uses ExternalId field instead of the unified Identity field used in other v2 identity endpoints. This is because the update endpoint needs to both find by externalId and potentially update the externalId value, making the specific field name more appropriate than the generic Identity field.

Learnt from: chronark
PR: #2146
File: apps/dashboard/lib/trpc/routers/api/setDefaultPrefix.ts:80-80
Timestamp: 2024-10-04T17:27:08.666Z
Learning: Ensure that audit log descriptions accurately reflect the action being performed, such as updating the defaultPrefix, and avoid incorrect references like 'name' when not applicable.

Learnt from: MichaelUnkey
PR: #3173
File: apps/docs/security/delete-protection.mdx:21-24
Timestamp: 2025-04-22T17:34:04.438Z
Learning: In the Unkey dashboard UI for enabling delete protection, the button/link to initiate the process is labeled "Enable Delete Protection" while the confirmation button is labeled "Enable API Delete Protection". The documentation should maintain these different labels to match the actual UI.

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

Learnt from: AkshayBandi027
PR: #2215
File: apps/dashboard/app/(app)/@breadcrumb/authorization/roles/[roleId]/page.tsx:28-29
Timestamp: 2024-10-08T15:33:04.290Z
Learning: In authorization/roles/[roleId]/update-role.tsx, the tag role-${role.id} is revalidated after updating a role to ensure that the caching mechanism is properly handled for roles.

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

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.

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: CR
PR: unkeyed/unkey#0
File: go/deploy/CLAUDE.md:0-0
Timestamp: 2025-07-21T18:05:58.236Z
Learning: Applies to go/deploy/**/*.{go,js,ts,tsx,py,sh,md,txt,json,yaml,yml,ini,env,conf,html,css,scss,xml,c,h,cpp,java,rb,rs,php,pl,sql} : Do not remove AIDEV-*s without explicit human instruction.

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.

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

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.

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.

Learnt from: Flo4604
PR: #3647
File: go/apps/api/openapi/openapi-generated.yaml:3569-3575
Timestamp: 2025-07-22T18:09:41.800Z
Learning: In the Unkey codebase, using non-standard HTTP status code 529 for internal-only endpoints is acceptable and should not be flagged as an issue in future reviews.

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.

Learnt from: CR
PR: unkeyed/unkey#0
File: go/deploy/CLAUDE.md:0-0
Timestamp: 2025-07-21T18:05:58.236Z
Learning: Applies to go/deploy/**/*.{env,sh,yaml,yml,json,conf,ini} : All environment variables MUST follow the format UNKEY_<SERVICE_NAME>_VARNAME.

Learnt from: CR
PR: unkeyed/unkey#0
File: go/deploy/CLAUDE.md:0-0
Timestamp: 2025-07-21T18:05:58.236Z
Learning: Applies to go/deploy/**/*.{go,js,ts,tsx,py,sh,md,txt,json,yaml,yml,ini,env,conf,html,css,scss,xml,c,h,cpp,java,rb,rs,php,pl,sql} : 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.

🧬 Code Graph Analysis (28)
go/apps/api/routes/v2_permissions_get_permission/403_test.go (1)
go/pkg/db/models_generated.go (1)
  • Permission (746-754)
go/apps/api/routes/v2_keys_remove_permissions/400_test.go (4)
go/pkg/db/types/null_string.go (1)
  • NullString (10-10)
go/pkg/testutil/seed/seed.go (1)
  • New (36-42)
go/pkg/uid/uid.go (1)
  • KeyPrefix (16-16)
go/apps/api/routes/v2_keys_remove_permissions/handler.go (1)
  • Request (22-22)
go/pkg/db/key_role_delete_many_by_key_and_role_ids.sql_generated.go (2)
go/pkg/hydra/store/db.go (1)
  • DBTX (8-13)
go/pkg/db/key_role_delete_all_by_key_id.sql_generated.go (1)
  • DeleteAllKeyRolesByKeyID (21-24)
go/apps/api/routes/v2_permissions_delete_permission/404_test.go (3)
go/apps/api/openapi/gen.go (1)
  • Permission (296-316)
go/pkg/db/models_generated.go (1)
  • Permission (746-754)
go/pkg/db/types/null_string.go (1)
  • NullString (10-10)
go/apps/api/routes/v2_keys_set_permissions/404_test.go (3)
go/pkg/db/types/null_string.go (1)
  • NullString (10-10)
go/pkg/testutil/seed/seed.go (1)
  • New (36-42)
go/pkg/uid/uid.go (1)
  • KeyPrefix (16-16)
go/apps/api/routes/v2_permissions_delete_role/401_test.go (1)
go/apps/api/openapi/gen.go (1)
  • Role (425-449)
go/apps/api/routes/v2_keys_add_permissions/401_test.go (4)
go/pkg/testutil/seed/seed.go (2)
  • CreateApiRequest (79-87)
  • CreateKeyRequest (183-200)
go/pkg/uid/uid.go (1)
  • TestPrefix (24-24)
go/pkg/db/permission_insert.sql_generated.go (1)
  • InsertPermissionParams (33-40)
go/pkg/db/types/null_string.go (1)
  • NullString (10-10)
go/apps/api/routes/v2_permissions_delete_role/403_test.go (1)
go/apps/api/openapi/gen.go (1)
  • Role (425-449)
go/pkg/db/role_find_many_by_name_with_perms.sql_generated.go (2)
go/pkg/db/types/null_string.go (1)
  • NullString (10-10)
go/pkg/hydra/store/db.go (1)
  • DBTX (8-13)
go/apps/api/routes/v2_permissions_delete_role/404_test.go (1)
go/apps/api/openapi/gen.go (1)
  • Role (425-449)
go/apps/api/routes/v2_keys_add_roles/200_test.go (2)
go/pkg/testutil/seed/seed.go (1)
  • CreateRoleRequest (360-366)
go/pkg/ptr/pointer.go (1)
  • P (49-51)
go/pkg/db/permission_insert.sql_generated.go (1)
go/pkg/db/types/null_string.go (1)
  • NullString (10-10)
go/apps/api/routes/v2_permissions_delete_permission/403_test.go (3)
go/pkg/db/types/null_string.go (1)
  • NullString (10-10)
go/apps/api/openapi/gen.go (1)
  • Permission (296-316)
go/pkg/db/models_generated.go (1)
  • Permission (746-754)
go/apps/api/routes/v2_keys_remove_permissions/200_test.go (5)
go/pkg/testutil/seed/seed.go (2)
  • CreatePermissionRequest (397-402)
  • New (36-42)
go/apps/api/routes/v2_keys_remove_permissions/handler.go (1)
  • Request (22-22)
go/pkg/db/types/null_string.go (1)
  • NullString (10-10)
go/pkg/uid/uid.go (1)
  • TestPrefix (24-24)
go/pkg/db/permission_insert.sql_generated.go (1)
  • InsertPermissionParams (33-40)
go/apps/api/routes/v2_permissions_get_role/404_test.go (1)
go/apps/api/openapi/gen.go (1)
  • Role (425-449)
go/apps/api/routes/v2_permissions_get_role/401_test.go (1)
go/apps/api/openapi/gen.go (1)
  • Role (425-449)
go/apps/api/routes/v2_permissions_create_role/handler.go (2)
go/pkg/fault/wrap.go (2)
  • Internal (75-89)
  • Public (97-111)
go/pkg/sim/events.go (1)
  • Event (11-19)
go/pkg/db/role_list.sql_generated.go (2)
go/pkg/db/types/null_string.go (1)
  • NullString (10-10)
go/pkg/hydra/store/db.go (1)
  • DBTX (8-13)
go/apps/api/routes/v2_permissions_list_roles/200_test.go (1)
go/pkg/db/types/null_string.go (1)
  • NullString (10-10)
go/apps/api/routes/v2_permissions_delete_permission/200_test.go (2)
go/apps/api/openapi/gen.go (1)
  • Permission (296-316)
go/pkg/db/models_generated.go (1)
  • Permission (746-754)
go/apps/api/routes/v2_keys_create_key/handler.go (5)
go/apps/api/openapi/gen.go (1)
  • Permission (296-316)
go/pkg/db/models_generated.go (1)
  • Permission (746-754)
go/pkg/db/permission_insert.sql_generated.go (1)
  • InsertPermissionParams (33-40)
go/pkg/db/types/null_string.go (1)
  • NullString (10-10)
go/pkg/cli/flag.go (1)
  • String (331-363)
go/apps/api/routes/v2_keys_add_permissions/404_test.go (4)
go/pkg/uid/uid.go (2)
  • PermissionPrefix (27-27)
  • KeyPrefix (16-16)
go/pkg/db/permission_insert.sql_generated.go (1)
  • InsertPermissionParams (33-40)
go/pkg/db/types/null_string.go (1)
  • NullString (10-10)
go/apps/api/routes/v2_keys_add_permissions/handler.go (1)
  • Request (25-25)
go/apps/api/routes/v2_keys_set_permissions/200_test.go (4)
go/pkg/db/permission_insert.sql_generated.go (1)
  • InsertPermissionParams (33-40)
go/pkg/db/types/null_string.go (1)
  • NullString (10-10)
go/pkg/testutil/seed/seed.go (1)
  • New (36-42)
go/pkg/uid/uid.go (2)
  • TestPrefix (24-24)
  • PermissionPrefix (27-27)
go/apps/api/routes/v2_permissions_list_permissions/200_test.go (2)
go/pkg/db/types/null_string.go (1)
  • NullString (10-10)
go/pkg/cli/flag.go (1)
  • String (331-363)
go/apps/api/routes/v2_permissions_get_role/200_test.go (3)
go/pkg/db/types/null_string.go (1)
  • NullString (10-10)
go/apps/api/openapi/gen.go (1)
  • Role (425-449)
go/pkg/db/models_generated.go (1)
  • Role (813-820)
go/apps/api/routes/v2_keys_set_roles/200_test.go (3)
go/pkg/rbac/query.go (1)
  • T (84-90)
internal/db/src/types.ts (1)
  • InsertRole (26-26)
go/pkg/db/role_insert.sql_generated.go (1)
  • InsertRoleParams (30-36)
go/pkg/db/querier_generated.go (11)
go/pkg/hydra/store/db.go (1)
  • DBTX (8-13)
go/pkg/db/key_permission_delete_many_by_key_and_permission_ids.sql_generated.go (1)
  • DeleteManyKeyPermissionByKeyAndPermissionIDsParams (18-21)
go/pkg/db/key_role_delete_many_by_key_and_role_ids.sql_generated.go (1)
  • DeleteManyKeyRolesByKeyAndRoleIDsParams (18-21)
go/pkg/db/role_find_many_by_id_or_name_with_perms.sql_generated.go (2)
  • FindManyRolesByIdOrNameWithPermsParams (37-40)
  • FindManyRolesByIdOrNameWithPermsRow (42-50)
go/pkg/db/role_find_many_by_name_with_perms.sql_generated.go (2)
  • FindManyRolesByNamesWithPermsParams (34-37)
  • FindManyRolesByNamesWithPermsRow (39-47)
go/pkg/db/permission_find_by_id_or_slug.sql_generated.go (1)
  • FindPermissionByIdOrSlugParams (18-21)
go/pkg/db/models_generated.go (1)
  • Permission (746-754)
go/pkg/db/permission_find_by_slugs.sql_generated.go (1)
  • FindPermissionsBySlugsParams (17-20)
go/pkg/db/role_find_by_id_or_name_with_perms.sql_generated.go (2)
  • FindRoleByIdOrNameWithPermsParams (36-39)
  • FindRoleByIdOrNameWithPermsRow (41-49)
go/pkg/db/role_list.sql_generated.go (2)
  • ListRolesParams (36-39)
  • ListRolesRow (41-49)
go/pkg/db/role_list_by_key_id.sql_generated.go (1)
  • ListRolesByKeyIDRow (35-43)
go/apps/api/openapi/gen.go (2)
go/pkg/codes/unkey_data.go (1)
  • Data (87-128)
go/pkg/db/models_generated.go (2)
  • Permission (746-754)
  • Role (813-820)
⏰ 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). (2)
  • GitHub Check: Test Agent Local / test_agent_local
  • GitHub Check: Test API / API Test Local
🔇 Additional comments (165)
go/pkg/db/plugins/bulk-insert/template_test.go (1)

28-28: LGTM: Test coverage updated for ValuesFields functionality

The addition of ValuesFields to all test cases properly covers the new field in the TemplateData struct. Setting it to the same values as Fields is appropriate for these test scenarios where insertion fields and overall fields are identical.

Also applies to: 51-51, 71-71, 113-113

go/apps/api/routes/v2_permissions_get_role/403_test.go (1)

56-56: LGTM! Field renaming aligns with PR objectives.

The change from RoleId to Role properly supports the PR objective of allowing either role ID or role name in permission-related requests. This change is consistent across both test cases.

Also applies to: 83-83

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

189-189: LGTM! Minor formatting improvement.

The added blank line improves code readability by separating the error handling block from the subsequent logic.

go/apps/api/routes/v2_permissions_list_roles/200_test.go (1)

14-14: LGTM! Type migration improves consistency.

The change from sql.NullString to dbtype.NullString aligns with the broader codebase standardization effort. The dbtype.NullString type provides consistent JSON and SQL handling across the application.

Also applies to: 61-61

go/apps/api/routes/v2_permissions_delete_role/403_test.go (1)

62-62: LGTM! Consistent field renaming supports flexible role identification.

The change from RoleId to Role is consistent with the PR objective and matches similar changes across other test files. This allows the endpoint to accept either role ID or role name.

Also applies to: 86-86

go/apps/api/routes/v2_permissions_delete_role/401_test.go (1)

27-27: LGTM! Final consistent field renaming.

The change from RoleId to Role maintains consistency with all other test files and supports the unified approach to role identification using either ID or name.

go/apps/api/routes/v2_permissions_get_role/401_test.go (1)

26-26: LGTM: Field name update aligns with API model changes.

The field name change from RoleId to Role is consistent with the broader API refactoring that now accepts either role ID or name in the unified Role field.

go/apps/api/routes/v2_permissions_get_role/404_test.go (1)

40-40: LGTM: Field name update maintains test coverage.

The field name change from RoleId to Role is consistent with the API model updates. The test continues to appropriately verify 404 responses for non-existent role identifiers.

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

14-14: LGTM: Import added for standardized nullable string type.

The addition of the dbtype import aligns with the codebase standardization of nullable string handling.


68-68: LGTM: Type change standardizes nullable string handling.

The change from sql.NullString to dbtype.NullString is part of the broader codebase standardization for nullable string fields.


112-112: LGTM: Field name updates align with API model changes.

The field name changes from RoleId to Role are consistent with the API refactoring that now accepts either role ID or name in the unified Role field. The test logic appropriately covers both role deletion scenarios.

Also applies to: 171-171

go/apps/api/routes/v2_permissions_list_permissions/403_test.go (2)

14-14: LGTM: Import standardization for nullable string type.

The import change to use the dbtype package aligns with the codebase-wide standardization of nullable string handling.


40-40: LGTM: Type changes standardize nullable string handling.

The changes from sql.NullString to dbtype.NullString are consistent with the project-wide type standardization for nullable string fields in database operations.

Also applies to: 81-81

go/pkg/db/queries/key_role_delete_many_by_key_and_role_ids.sql (1)

1-3: LGTM! Efficient batch deletion query.

The SQL query is well-structured for bulk deletion operations. Using sqlc.slice for the role_ids parameter enables efficient batch processing while maintaining proper parameterization to prevent SQL injection.

go/pkg/db/models_generated.go (2)

13-13: Proper import for custom nullable string type.

The addition of the dbtype import supports the migration from standard sql.NullString to the custom dbtype.NullString type.


747-754: Consistent migration to custom nullable string type.

The change from sql.NullString to dbtype.NullString for the Description field aligns with the broader codebase migration to custom nullable types that provide enhanced JSON and SQL marshaling capabilities.

go/apps/api/routes/v2_permissions_delete_role/404_test.go (2)

48-48: Field name updated to support unified role identification.

The change from RoleId to Role aligns with the API simplification allowing either role ID or slug to be used for role identification.


83-83: Consistent field naming across test cases.

The field name change maintains consistency with the API model updates and supports the unified role identification approach.

go/apps/api/routes/v2_permissions_create_role/handler.go (3)

6-6: Import added for improved error message formatting.

The fmt package import supports better error message formatting with quoted role names.


92-92: Enhanced error message with proper role name quoting.

The use of fmt.Sprintf with %q provides clearer error messages by properly quoting role names, improving user experience when duplicate role errors occur.


110-110: Consistent use of audit log constants.

Replacing hardcoded strings with auditlog.RoleCreateEvent and auditlog.RoleResourceType constants improves maintainability and ensures consistent audit logging across the codebase.

Also applies to: 120-120

go/apps/api/routes/v2_keys_add_roles/401_test.go (1)

51-51: Simplified role specification in request payload.

The change from a slice of structs to a simple slice of strings aligns with the API model simplification, allowing roles to be specified directly as strings (either IDs or names) rather than requiring structured objects.

go/apps/api/routes/v2_permissions_get_permission/403_test.go (3)

14-14: LGTM: Consistent nullable string type migration.

The import change from sql to dbtype aligns with the codebase-wide migration to use custom nullable string types for better consistency across database operations.


43-43: LGTM: Database operation updated with consistent type.

The change from sql.NullString to dbtype.NullString maintains the same functionality while aligning with the new type system being adopted across the codebase.


59-59: LGTM: Request field renamed for API consistency.

The field rename from PermissionId to Permission aligns with the PR objective to allow either permission ID or slug in permission-related requests, providing a unified field name for both use cases.

Also applies to: 86-86

go/apps/api/routes/v2_keys_set_roles/401_test.go (1)

29-29: LGTM: Simplified roles field structure.

The change from a complex struct slice with optional Id and Name fields to a simple string slice aligns with the API model simplification described in the PR. This makes the API more intuitive while maintaining the same functionality for role management.

go/apps/api/routes/v2_permissions_list_permissions/200_test.go (2)

13-13: LGTM: Consistent import for nullable string type.

The import change to use dbtype from the custom types package aligns with the codebase-wide standardization of nullable string handling.


63-63: LGTM: Database operations updated with consistent type.

All permission insertions have been consistently updated to use dbtype.NullString instead of sql.NullString for the Description field, maintaining the same functionality while adopting the standardized type system.

Also applies to: 76-76, 150-150

go/apps/api/routes/v2_keys_remove_roles/403_test.go (1)

69-69: LGTM: Consistent roles field simplification across all test cases.

All test cases in TestAuthorizationErrors have been consistently updated to use simple string slices for the Roles field instead of complex struct slices. This aligns with the API model simplification that standardizes role operations to use string arrays for role IDs/names, making the API more intuitive and consistent.

Also applies to: 122-122, 175-175, 228-228, 281-281, 334-334, 388-388

go/pkg/testutil/seed/seed.go (2)

14-14: LGTM: Import updated for consistent type system.

The import change to use dbtype from the custom types package ensures that seed utilities use the same standardized nullable string types as the rest of the codebase.


152-152: LGTM: Seed methods updated with consistent nullable string type.

Both CreateRootKey and CreatePermission methods have been updated to use dbtype.NullString for permission descriptions, ensuring that test data creation utilities align with the standardized type system used throughout the codebase.

Also applies to: 415-415

go/pkg/db/permission_insert.sql_generated.go (1)

11-11: LGTM: Type standardization aligns with codebase migration.

The migration from sql.NullString to dbtype.NullString for the Description field is consistent with the broader codebase standardization. The custom dbtype.NullString type provides enhanced JSON marshaling capabilities while maintaining SQL compatibility.

Also applies to: 38-38

go/pkg/db/sqlc.json (1)

44-52: LGTM: Proper SQLC configuration for type standardization.

The type override configuration correctly maps permissions.description to the custom dbtype.NullString type. This ensures consistent nullable string handling across all generated database operations while maintaining JSON marshaling compatibility.

go/apps/api/routes/v2_keys_set_roles/403_test.go (1)

69-69: LGTM: API simplification improves usability.

The change from complex role objects to simple string slices aligns with the PR objective of accepting either permission ID or slug. This simplification makes the API more intuitive while maintaining the same authorization testing coverage.

Also applies to: 97-97

go/apps/api/routes/v2_permissions_delete_permission/200_test.go (2)

13-13: LGTM: Type standardization using custom nullable string type.

The migration from sql.NullString to dbtype.NullString ensures consistent nullable string handling across the codebase with enhanced JSON marshaling capabilities.

Also applies to: 55-55, 113-113


67-67: LGTM: Field rename supports unified ID/slug handling.

The field name change from PermissionId to Permission aligns with the PR objective of accepting either permission ID or slug in permission-related requests, providing a more flexible API contract.

Also applies to: 126-126

go/apps/api/routes/v2_keys_add_roles/403_test.go (2)

60-60: LGTM: Consistent API simplification across authorization tests.

The change from complex role objects to simple string slices maintains comprehensive authorization error testing while simplifying the API contract. This supports the PR objective of accepting either role ID or slug in a unified format.

Also applies to: 124-124, 170-170, 216-216


42-56: LGTM: Improved test setup using helper methods.

The migration from manual database inserts to testutil/seed helpers improves test maintainability and readability while ensuring proper test data setup.

go/apps/api/openapi/spec/paths/v2/keys/setRoles/V2KeysSetRolesResponseData.yaml (1)

17-17: LGTM! Good schema centralization.

The change from inline role object definition to a reference to the common role schema improves maintainability and reduces duplication across the API specification.

go/apps/api/routes/v2_keys_set_permissions/401_test.go (3)

15-15: LGTM! Consistent type migration.

The change from database/sql import to dbtype package aligns with the codebase-wide migration to use internal nullable string types.


75-75: LGTM! Consistent nullable string type usage.

The change from sql.NullString to dbtype.NullString is consistent with the broader type migration across the codebase for better JSON marshaling/unmarshaling support.


80-81: LGTM! Simplified permissions structure.

The change from complex permission objects to a simple string slice ([]string{permissionID}) aligns perfectly with the PR objective to accept either permission ID or slug, making the API more user-friendly.

go/apps/api/routes/v2_permissions_delete_permission/403_test.go (3)

14-14: LGTM! Consistent import migration.

The change to use dbtype package import is consistent with the codebase-wide migration to internal nullable types.


44-44: LGTM! Consistent type usage.

The migration from sql.NullString to dbtype.NullString maintains consistency across the codebase.


60-60: LGTM! Field name aligns with PR objectives.

The change from PermissionId to Permission is semantically better and aligns with the PR objective to accept either permission ID or slug in permission-related requests.

Also applies to: 95-95

go/apps/api/routes/v2_permissions_delete_role/400_test.go (2)

41-41: LGTM! Clear comment update.

The comment clearly indicates the missing field in the context of the updated field name.


57-58: LGTM! Consistent field name standardization.

The change from RoleId to Role is consistent with the API standardization and aligns with the PR objective to support both role ID and role name references. The test description updates maintain clarity.

Also applies to: 60-60

go/apps/api/routes/v2_permissions_delete_permission/404_test.go (3)

14-14: LGTM! Consistent import standardization.

The migration to dbtype package maintains consistency across all test files.


47-47: LGTM! Consistent field name updates across all test cases.

The uniform change from PermissionId to Permission across all test scenarios maintains consistency and aligns with the API's new ability to accept either permission ID or slug.

Also applies to: 68-68, 104-104


93-93: LGTM! Consistent nullable type usage.

The change to dbtype.NullString maintains type consistency across the codebase.

go/pkg/db/queries/role_list.sql (2)

2-16: LGTM! Well-structured JSON aggregation for role permissions.

The SQL correctly uses COALESCE with JSON_ARRAYAGG to handle roles without permissions, returning an empty JSON array when no permissions exist. The correlated subquery properly joins the role-permission association table with the permissions table.


19-21: Standard cursor-based pagination implementation.

The id_cursor filtering and LIMIT 101 indicate proper cursor-based pagination where the extra item helps determine if there's a next page.

go/apps/api/routes/v2_permissions_get_role/200_test.go (5)

14-14: Appropriate use of custom dbtype package.

The import of dbtype aligns with the codebase's custom database type wrappers.


67-67: Consistent use of dbtype.NullString for nullable fields.

The change from sql.NullString to dbtype.NullString maintains consistency with the codebase's custom database types.


84-84: Request field properly updated to support flexible role identification.

The change from RoleId to Role aligns with the PR objective to accept either role ID or name, improving API flexibility.

Also applies to: 140-140


127-127: Role name simplified and follows naming conventions.

The change from "test.get.role.no.perms" to "rolewithoutpermissions" follows cleaner naming conventions and aligns with the test's purpose.


163-164: Test assertions correctly updated for new response structure.

The assertions properly check for an empty permissions slice, which matches the updated API response structure where permissions are returned as slices rather than nullable fields.

go/pkg/db/queries/role_find_many_by_id_or_name_with_perms.sql (1)

1-21: Well-implemented batch role lookup with flexible identification.

The query correctly implements the PR objective of accepting either role IDs or names. The JSON aggregation pattern is consistent with other role queries, and the use of sqlc.slice for variable-length IN clauses is appropriate for batch operations.

go/apps/api/openapi/spec/paths/v2/keys/removeRoles/V2KeysRemoveRolesResponseData.yaml (1)

17-17: Good use of shared schema reference for consistency.

Using $ref to the common role schema promotes consistency across the API specification and reduces duplication. This aligns with the broader refactoring to unify role representations.

go/pkg/db/queries/role_find_many_by_name_with_perms.sql (1)

1-18: Clean implementation for name-based role lookup.

The query follows the established JSON aggregation pattern and correctly filters roles by name only. The use of sqlc.slice for the names parameter properly handles variable-length IN clauses for batch operations.

go/apps/api/routes/v2_permissions_delete_role/handler.go (3)

66-69: LGTM! Enhanced role lookup supports ID or name.

The change from a simple role ID lookup to FindRoleByIdOrNameWithPerms aligns perfectly with the PR objective to support both role IDs and names/slugs. The query properly scopes to the authorized workspace and includes permissions data in a single query, improving efficiency.


84-84: Correct usage of resolved role ID throughout database operations.

All database operations now consistently use role.ID from the resolved role instead of req.Role, ensuring they operate on the actual role record found by the flexible lookup. This prevents potential issues if the request contained a role name that needed to be resolved to an ID.

Also applies to: 92-92, 100-100


111-111: Good standardization of audit log constants.

Replacing hardcoded string literals with predefined constants (auditlog.RoleDeleteEvent, auditlog.RoleResourceType) improves maintainability and reduces the risk of typos in audit logging. The display message correctly uses the resolved role.ID.

Also applies to: 116-116, 121-122

go/apps/api/routes/v2_permissions_create_permission/handler.go (3)

14-14: Good migration to internal nullable string types.

The change from sql.NullString to dbtype.NullString standardizes the codebase to use internal types consistently. This provides better control over database type handling and aligns with the broader refactoring across permission-related handlers.

Also applies to: 78-78


99-99: Excellent standardization of audit log constants.

Using auditlog.PermissionCreateEvent and auditlog.PermissionResourceType instead of hardcoded strings improves maintainability and prevents typos in audit logging.

Also applies to: 109-109


111-111: Audit log resource Name is intentionally using the slug for permissions

The change in v2_permissions_create_permission (Name: req.Slug, DisplayName: req.Name) aligns with our pattern of logging a machine-friendly identifier separately from the human-readable label. In contrast, roles have no slug, so both Name and DisplayName remain req.Name. No further edits are required here.

go/pkg/db/queries/role_find_by_id_or_name_with_perms.sql (1)

1-21: Well-designed SQL query supporting flexible role lookup.

This query effectively implements the core requirement of finding roles by either ID or name while efficiently aggregating permissions as JSON. Key strengths:

  1. Flexible search: Uses sqlc.arg('search') for both ID and name comparison
  2. Efficient aggregation: Single query retrieves role and permissions together
  3. Proper null handling: COALESCE with JSON_ARRAY() handles roles without permissions
  4. Complete permission data: Includes all relevant permission fields (id, name, slug, description)
  5. Workspace scoping: Properly restricts results to the authorized workspace

The JSON aggregation approach is efficient and avoids N+1 query problems.

go/apps/api/openapi/spec/paths/v2/keys/addRoles/V2KeysAddRolesResponseData.yaml (1)

16-16: Good schema centralization with shared role reference.

Replacing the inline role object definition with a reference to the common role schema improves maintainability and ensures consistent role representation across all API endpoints. This aligns well with the PR's goal to standardize permission and role schemas.

go/apps/api/routes/v2_keys_remove_roles/401_test.go (1)

66-66: Excellent test simplification aligning with API changes.

The conversion from complex role structs with optional Id and Name fields to simple string slices makes the test code cleaner and aligns perfectly with the simplified API request format. This reduces test complexity while maintaining the same test coverage for authentication error scenarios.

Also applies to: 116-116, 167-167, 218-218, 269-269, 324-324

go/apps/api/routes/v2_keys_create_key/handler.go (4)

20-20: LGTM! Import addition for custom nullable string type.

The addition of the dbtype import aligns with the codebase migration from sql.NullString to the custom dbtype.NullString type for nullable database fields.


367-370: LGTM! Improved consistency with full Permission struct.

The change from using db.FindPermissionsBySlugsRow to db.Permission improves consistency throughout the permission handling logic.


435-435: LGTM! Standardized audit log resource types.

Replacing hardcoded strings with constants from the auditlog package (auditlog.KeyResourceType, auditlog.PermissionResourceType, auditlog.RoleResourceType, auditlog.APIResourceType) improves maintainability and consistency across the codebase.

Also applies to: 442-442, 522-522, 529-529, 564-564, 571-571


392-400: Please manually verify UpdatedAtM handling in InsertPermissions SQL

I wasn’t able to locate the generated InsertPermissions implementation to confirm whether the updated_at_m column is part of the INSERT. Before merging, please check the SQL in the generated bulk-insert file for permissions and ensure that:

  • If updated_at_m is listed in the INSERT columns, your struct provides a valid timestamp.
  • If it’s omitted, the zero-value (Valid: false) is correct.
go/apps/api/routes/v2_permissions_list_roles/handler.go (2)

5-5: LGTM! Import addition for JSON unmarshaling.

The encoding/json import is correctly added to support the new inline permission unmarshaling logic.


91-96: LGTM! Simplified role response structure.

The removal of the CreatedAt field aligns with the API model simplifications mentioned in the PR objectives.

go/apps/api/routes/v2_permissions_get_permission/200_test.go (4)

13-13: LGTM! Import change aligns with codebase migration.

The addition of dbtype import aligns with the codebase-wide migration from sql.NullString to the custom dbtype.NullString type.


42-56: LGTM! Improved test setup efficiency.

Creating the permission once before running multiple subtests is more efficient and follows good testing practices.


59-59: LGTM! Comprehensive test coverage for unified Permission field.

The subtests effectively verify that both permission ID and slug retrieval work correctly with the unified Permission field, aligning with the PR objective to accept either permission ID or slug.

Also applies to: 86-106


53-53: LGTM! Consistent use of dbtype.NullString.

The migration from sql.NullString to dbtype.NullString is consistent with the broader codebase changes.

Also applies to: 119-119

go/apps/api/routes/v2_keys_set_permissions/403_test.go (4)

13-13: LGTM! Import changes align with test modernization.

The addition of dbtype and seed imports, along with removal of unused imports, aligns with the test modernization using seed helpers and the codebase migration to custom nullable types.

Also applies to: 15-15


36-61: LGTM! Simplified test setup using seed helpers.

The refactoring from manual database insertions to using h.CreateApi and h.CreateKey seed helpers significantly simplifies the test setup and improves maintainability.


63-63: LGTM! Corrected permission ID prefix.

The change from uid.TestPrefix to uid.PermissionPrefix is the correct prefix for permission entities and aligns with proper UID conventions.


69-69: LGTM! Simplified request structure and consistent nullable type usage.

The simplification of the Permissions field from complex objects to a string slice aligns with the API model changes, and the use of dbtype.NullString is consistent with the codebase migration.

Also applies to: 75-75

go/apps/api/routes/v2_keys_add_permissions/401_test.go (3)

13-13: LGTM! Import changes support test modernization.

The addition of dbtype and seed imports, with removal of unused imports, supports the migration to seed helpers and custom nullable types.

Also applies to: 15-15


36-53: LGTM! Simplified test setup with seed helpers.

The use of h.CreateApi and h.CreateKey seed helpers significantly improves test maintainability compared to manual database insertions.


61-61: LGTM! Consistent type usage and simplified request structure.

The use of dbtype.NullString and simplified Permissions field as a string slice aligns with the broader codebase changes and API model simplifications.

Also applies to: 67-67

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

64-67: Good optimization using a single query.

The refactor to use FindRoleByIdOrNameWithPerms improves performance by fetching the role and its permissions in a single database query.

go/pkg/db/queries/role_list_by_key_id.sql (1)

1-21: Well-structured SQL query with efficient JSON aggregation.

The query efficiently retrieves roles with their permissions using JSON aggregation, properly handles empty permission sets, and maintains good performance characteristics.

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

30-30: Test updates correctly reflect the simplified API structure.

The changes appropriately update the test cases to use the new string-based roles format and include the required RBAC permission.

Also applies to: 121-121, 161-161

go/apps/api/routes/v2_keys_update_key/handler.go (2)

416-440: Excellent audit logging for permission creation.

The implementation provides comprehensive audit trails for each created permission with all relevant metadata.


461-468: Good practice using consistent timestamps for batch inserts.

Using a single timestamp ensures all permissions in the batch have identical creation times, which is appropriate for atomic operations.

go/apps/api/routes/v2_keys_remove_permissions/404_test.go (1)

15-15: Test updates align with API simplification and type improvements.

The changes correctly update tests to use the new string-based permissions format and custom nullable types, maintaining good test coverage.

Also applies to: 38-38, 60-61, 98-99, 132-132, 235-235

go/apps/api/routes/v2_keys_add_permissions/400_test.go (4)

15-15: LGTM - Import aligns with type standardization.

The addition of the dbtype import supports the migration from sql.NullString to dbtype.NullString throughout the codebase, which provides better type consistency.


37-37: LGTM - Enhanced RBAC permission scope.

The addition of "rbac.*.add_permission_to_key" permission to the root key creation aligns with the PR's objective to enable permission creation when the root key has the necessary permissions.


188-189: LGTM - API simplification improves usability.

The change from complex permission objects with optional Id and Slug fields to a simple string array makes the API more intuitive and easier to use. OpenAPI schema validation ensures input integrity.


183-183: LGTM - Type standardization for nullable strings.

The migration from sql.NullString to dbtype.NullString improves consistency across the codebase while maintaining the same underlying functionality.

go/apps/api/openapi/spec/paths/v2/permissions/deleteRole/V2PermissionsDeleteRoleRequestBody.yaml (3)

3-5: LGTM - Field name simplification improves API consistency.

Renaming roleId to role makes the API more intuitive since the field now accepts either role IDs or names, aligning with the broader API unification effort.


7-8: LGTM - Validation pattern matches role naming requirements.

The regex pattern "^[a-zA-Z][a-zA-Z0-9_-]*$" and minLength: 3 correctly implement the role naming validation requirements, allowing names that start with a letter followed by letters, numbers, underscores, or hyphens.


10-12: LGTM - Clear documentation for dual-purpose field.

The updated description clearly explains that the field accepts either a role ID (starting with 'role_') or a role name, providing better guidance for API users while maintaining important deletion warnings.

go/apps/api/openapi/spec/paths/v2/keys/removeRoles/V2KeysRemoveRolesRequestBody.yaml (2)

20-20: LGTM - Schema simplification with appropriate constraints.

The addition of maxItems: 100 provides reasonable abuse protection, and the simplification from complex role objects to strings with proper validation patterns improves API usability while maintaining data integrity.

Also applies to: 28-32


32-32: LGTM - Concise description matches simplified schema.

The simplified description "Specify the role by name." clearly communicates the expected input format for the string-based role specification.

go/apps/api/routes/v2_keys_set_roles/404_test.go (2)

81-81: LGTM - Consistent API simplification across test cases.

The conversion from complex role objects with Id and Name pointers to simple string arrays consistently applies the API simplification pattern across all test scenarios, improving test readability and maintainability.

Also applies to: 105-105, 129-129, 192-192, 237-237, 259-259, 284-284, 309-309, 334-334


119-119: LGTM - Improved error message assertions.

Using fmt.Sprintf to format expected error messages with specific role identifiers provides more precise validation of error responses, ensuring exact matching rather than generic substring checks.

Also applies to: 143-143, 251-251, 273-273, 298-298, 348-348

go/apps/api/routes/v2_keys_remove_permissions/400_test.go (2)

13-13: LGTM - Consistent type standardization.

The addition of dbtype import supports the migration to standardized nullable string types, maintaining consistency with other test files in this PR.


167-167: LGTM - Aligned with API simplification pattern.

The change from sql.NullString to dbtype.NullString and the simplification of the permissions field from complex objects to a string array maintains consistency with the broader API refactoring effort.

Also applies to: 174-175

go/apps/api/routes/v2_keys_remove_roles/400_test.go (2)

35-35: LGTM! Proper permission scope added.

The addition of "rbac.*.remove_role_from_key" permission to the root key creation is necessary for the test to have proper authorization for role removal operations.


86-86: LGTM! Consistent role representation simplification.

The conversion from complex role structures to simple string slices ([]string) aligns with the API simplification goals. All test cases maintain their coverage while using the cleaner format that accepts either role IDs or names directly.

Also applies to: 114-114, 185-185, 222-222, 259-259, 289-289, 335-335, 383-383

go/apps/api/openapi/spec/paths/v2/keys/setRoles/V2KeysSetRolesRequestBody.yaml (1)

28-32: LGTM! Proper validation pattern and API simplification.

The schema correctly implements the role name validation pattern ^[a-zA-Z][a-zA-Z0-9_-]*$ which matches the established validation standards in the codebase. The simplification to string-based role references with appropriate length constraints (3-255 characters) aligns with the API streamlining objectives.

go/apps/api/routes/v2_keys_add_roles/404_test.go (3)

36-36: LGTM! Correct permission scope for add roles operation.

The addition of "rbac.*.add_role_to_key" permission ensures proper authorization for the add roles test scenarios.


48-48: LGTM! Consistent API simplification across test cases.

The conversion to string slices for role representation maintains comprehensive test coverage while aligning with the simplified API design. All scenarios including error cases, cross-workspace isolation, and multi-role operations are properly tested.

Also applies to: 88-88, 126-126, 164-164, 212-212, 261-261, 311-311, 365-365, 415-415


298-302: LGTM! Improved variable naming for clarity.

The variable naming changes from ID-based to name-based variables (validName, invalidName) improve code readability and align with the string-based role representation.

Also applies to: 307-307, 324-324

go/pkg/db/key_role_delete_many_by_key_and_role_ids.sql_generated.go (1)

27-41: LGTM! Well-implemented batch deletion method.

The generated method properly handles:

  • Dynamic IN clause construction for variable-length role ID slices
  • Empty slice edge case by replacing with NULL
  • Consistent parameter binding and error handling
  • Follows established patterns from similar batch operations

This enables efficient bulk deletion of key-role associations, improving performance over individual delete operations.

go/apps/api/routes/v2_keys_remove_roles/404_test.go (3)

35-35: LGTM! Consistent permission scoping.

The addition of "rbac.*.remove_role_from_key" permission maintains consistency with the RBAC permission model across test files.


59-59: LGTM! Comprehensive error scenario coverage with simplified API.

The string-based role representation maintains all critical error test scenarios including cross-workspace isolation, non-existent roles by ID/name, and multi-role operations with partial failures.

Also applies to: 98-98, 138-138, 188-188, 238-238, 288-288, 338-338


324-328: LGTM! Clear and consistent variable naming.

The variable naming improvements (validName, nonExistentRoleName) enhance test readability and clearly indicate the test scenarios being exercised.

Also applies to: 334-334

go/apps/api/routes/v2_keys_add_roles/200_test.go (1)

34-34: LGTM! Permission scope correctly updated.

The addition of "rbac.*.add_role_to_key" permission aligns with the enhanced RBAC checks in the handler.

go/apps/api/routes/v2_keys_remove_roles/200_test.go (1)

35-35: LGTM! Permission scope correctly updated.

The addition of "rbac.*.remove_role_from_key" permission is consistent with the enhanced RBAC checks.

go/apps/api/routes/v2_keys_remove_permissions/200_test.go (4)

13-13: LGTM! Import change aligns with codebase standardization.

The switch from database/sql to dbtype for nullable string types is consistent with the codebase-wide migration to use the custom dbtype.NullString wrapper.


35-35: Appropriate RBAC permission added for test authorization.

The addition of "rbac.*.remove_permission_from_key" permission to the root key is correct and necessary for testing the remove permissions endpoint.


72-75: Request structure correctly simplified to match API schema.

The change from complex permission objects to a simple string slice aligns with the API model simplifications across the codebase.


259-259: Consistent use of dbtype.NullString for nullable fields.

The migration from sql.NullString to dbtype.NullString is properly applied here, maintaining consistency with the database model changes across the codebase.

Also applies to: 270-270

go/apps/api/routes/v2_keys_set_permissions/404_test.go (2)

35-35: Correct RBAC permissions for set operations.

The root key appropriately includes both "rbac.*.remove_permission_from_key" and "rbac.*.add_permission_to_key" permissions, which are necessary for the set operation that may both add and remove permissions.


59-61: Request payload correctly simplified.

The change to use permission IDs directly as strings in the request aligns with the API schema simplification.

Also applies to: 109-111

go/apps/api/routes/v2_keys_add_permissions/403_test.go (2)

36-44: Good refactoring to use test helpers.

The migration from manual database operations to using seed helper functions improves test maintainability and reduces boilerplate code.

Also applies to: 46-61


74-76: Request structure properly updated.

The simplified permission array format is consistently applied across all test cases.

go/apps/api/routes/v2_keys_set_roles/handler.go (3)

60-66: Improved key lookup with explicit null handling.

The change to use FindKeyByIdOrHash with explicit sql.NullString parameters is more robust and type-safe than the previous approach.


117-127: Efficient batch role resolution.

Good optimization using FindManyRolesByNamesWithPerms to fetch all roles in a single query instead of individual lookups.


198-201: Efficient bulk role removal.

The use of DeleteManyKeyRolesByKeyAndRoleIDs for batch deletion is a good performance optimization compared to individual deletions.

go/pkg/db/role_list.sql_generated.go (2)

14-28: Efficient permission aggregation in role query.

The use of JSON_ARRAYAGG with COALESCE to aggregate permissions into a JSON array is an excellent optimization, reducing the need for N+1 queries when fetching roles with their permissions.


41-49: LGTM! Generated struct properly handles JSON permissions.

The new ListRolesRow struct correctly includes the aggregated permissions data. While interface{} is less type-safe than json.RawMessage, this is acceptable for sqlc-generated code.

go/apps/api/routes/v2_keys_set_permissions/200_test.go (6)

13-13: LGTM! Consistent with codebase migration to custom database types.

The import of dbtype aligns with the project-wide shift from sql.NullString to dbtype.NullString for better JSON and SQL compatibility.


35-35: LGTM! Proper RBAC permissions for comprehensive testing.

The additional permissions (rbac.*.remove_permission_from_key, rbac.*.add_permission_to_key, rbac.*.create_permission) are necessary for testing the full range of permission-setting operations, including removal, addition, and on-the-fly creation.


64-94: LGTM! Consistent migration to dbtype.NullString.

The permission creation properly uses dbtype.NullString instead of sql.NullString, which is consistent with the project-wide database type migration. The permission setup with matching name and slug values follows the correct pattern.


111-114: LGTM! Simplified permissions request structure.

The refactoring from complex permission objects to simple string arrays (permission slugs) significantly improves API usability while maintaining all necessary functionality. This aligns with the broader API simplification effort.


128-139: LGTM! More robust assertion logic.

The introduction of the contains helper function makes the tests more resilient to response ordering variations, which is a best practice since database query results don't guarantee order unless explicitly specified.


180-432: LGTM! Consistent application of migration patterns.

All remaining changes properly apply the established patterns:

  • Consistent use of dbtype.NullString for nullable descriptions
  • Simplified permission requests using string arrays
  • Proper use of uid.PermissionPrefix for permission ID generation
  • Maintained test coverage for all scenarios including on-the-fly permission creation

The changes preserve all test functionality while aligning with the API simplification effort.

go/apps/api/openapi/spec/paths/v2/keys/addRoles/V2KeysAddRolesRequestBody.yaml (1)

28-32: LGTM! Proper schema simplification with robust validation.

The refactoring from complex role objects to simple strings significantly improves API usability. The validation is comprehensive:

  • Regex pattern ensures role names start with a letter and contain only valid characters
  • Appropriate length constraints (3-255 characters)
  • Clear documentation about specifying roles by name

This aligns with the OpenAPI schema validation patterns used throughout the Unkey codebase.

go/apps/api/routes/v2_keys_set_roles/200_test.go (1)

74-323: LGTM! Consistent role request simplification.

All test cases properly apply the simplified request structure using Roles: []string{...} instead of complex objects. The changes maintain test functionality while aligning with the API schema simplification, and the variable naming (roleName) is consistent throughout.

go/pkg/db/role_find_by_id_or_name_with_perms.sql_generated.go (2)

13-34: LGTM! Well-structured SQL query with proper aggregation.

The SQL query correctly:

  • Scopes results by workspace_id for security
  • Supports flexible role lookup by either ID or name
  • Aggregates permissions using JSON_ARRAYAGG with proper field mapping
  • Handles the no-permissions case with COALESCE and empty JSON_ARRAY()
  • Uses appropriate table joins for role-permission relationships

36-86: LGTM! Proper Go implementation of the SQL query.

The Go code correctly implements the database query:

  • Parameter struct appropriately uses a single Search field for flexible ID/name lookup
  • Result struct includes all necessary fields with proper database tags
  • Permissions field as interface{} is appropriate for JSON data handling
  • Query execution properly passes the search parameter for both ID and name comparisons
  • Complete field scanning ensures all data is captured
go/apps/api/routes/v2_keys_add_permissions/404_test.go (4)

14-14: LGTM! Consistent dbtype import.

The import aligns with the project-wide migration to custom database types for better compatibility.


36-36: LGTM! Proper RBAC permissions for add_permissions endpoint.

The addition of "rbac.*.add_permission_to_key" permission is necessary for testing the add permissions functionality.


46-62: LGTM! Consistent migration patterns applied.

The changes properly apply established patterns:

  • Use of uid.PermissionPrefix for permission ID generation
  • Migration to dbtype.NullString for nullable descriptions
  • Simplified request structure using string arrays for permissions

106-119: LGTM! Consistent test patterns maintained.

The second test case correctly follows the same migration patterns while testing cross-workspace isolation scenarios. The permission creation and request structure align with the established patterns.

go/apps/api/routes/v2_keys_add_permissions/handler.go (7)

58-64: Good change to support flexible key lookup.

The switch from FindKeyByID to FindKeyByIdOrHash allows the endpoint to accept either a key ID or hash, improving API flexibility. The explicit null hash handling is correct.


85-110: Well-structured RBAC permission check.

The combined permission requirement (update key + add permission to key) provides proper security controls and prevents unauthorized permission escalation.


120-129: Efficient bulk permission lookup implementation.

The use of FindPermissionsBySlugs for batch retrieval is a good optimization over individual queries.


131-148: Correct handling of permission resolution by ID or slug.

The logic properly identifies missing permissions and handles the dual ID/slug reference pattern.


191-282: Excellent transaction handling with bulk operations.

The atomic transaction ensures data consistency, bulk inserts improve performance, and the ON DUPLICATE KEY UPDATE clause provides idempotency. The comprehensive audit logging properly tracks both permission creation and assignment events.


296-325: Proper response construction with full permission details.

The response correctly merges existing and newly added permissions, including optional descriptions.


158-189: Permission name validation is enforced by the OpenAPI schema

  • All permission names (used as both Name and Slug) are validated at the schema layer via pattern: "^[a-zA-Z0-9_]+$" and length constraints (1–100) during the zen.BindBody call.
  • Invalid values cannot reach this handler, so no additional defensive checks are needed here.
go/pkg/db/querier_generated.go (1)

42-56: Generated interface changes align well with API refactoring.

The new batch deletion methods, enriched role queries with permissions JSON, and flexible ID/slug lookups support the broader API simplification. The change to return full Permission structs from FindPermissionsBySlugs (line 393) provides richer data for API responses.

Also applies to: 317-373, 479-501, 809-809, 1186-1228

go/apps/api/openapi/openapi-generated.yaml (9)

176-185: EmptyResponse wiring looks good
The wrapper correctly re-uses the new EmptyResponse component – nothing to add here.


805-814: OK – consistent use of EmptyResponse
No issues spotted.


1473-1482: Response wrapper OK
Nothing to flag.


1987-1991: 👍 New EmptyResponse component is fine


2462-2478: LGTM – response definition wiring is correct


2492-2496: LGTM


2526-2529: LGTM


1408-1422: Regex here rejects valid slugs containing : or *
Other endpoints allow documents:* style identifiers via ^[a-zA-Z0-9_:\\-\\.\\*]+$, but the delete-permission endpoint uses the narrower ^[a-zA-Z][a-zA-Z0-9._-]*$. Deleting such a permission would be impossible through the API.

-                    pattern: "^[a-zA-Z][a-zA-Z0-9._-]*$"
+                    pattern: "^[a-zA-Z0-9_:\\-\\.\\*]+$"
⛔ Skipped due to learnings
Learnt from: chronark
PR: unkeyed/unkey#3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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.

577-589: Review comment not applicable—API only accepts role names, not IDs
The roles array is validated and looked up via FindRolesByNames against the Name column, so passing a role ID will fail. The OpenAPI snippet and handler logic correctly document name-based assignment. If ID support is desired, the handler must be updated first; otherwise, no docs change is needed.

Likely an incorrect or invalid review comment.

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

127-128: LGTM! EmptyResponse standardization improves consistency.

The introduction of the EmptyResponse type alias provides a unified way to handle empty response objects across the API, which improves consistency and maintainability. The map[string]interface{} type allows for future extensibility while maintaining clear "empty by design" semantics.


741-743: Excellent API simplification for permission operations.

The changes from complex permission objects to simple []string arrays significantly improve API usability while maintaining full functionality:

  • Flexibility: Supporting both permission IDs and slugs gives developers choice in how they reference permissions
  • Simplicity: String arrays are much easier to work with than complex objects
  • Security: Auto-creation behavior is properly documented with 403 error handling when root key lacks permissions
  • Consistency: Same pattern applied across add, remove, and set operations

This is a great example of API design that balances power with simplicity.

Also applies to: 995-996, 1087-1088, 1095-1096


776-776: Proper use of shared Permission and Role structs in responses.

The response data types now correctly reference the shared Permission and Role structs, which:

  • Maintains consistency with the database models (as seen in go/pkg/db/models_generated.go)
  • Provides complete information including IDs, names, descriptions, and other metadata
  • Ensures type safety throughout the codebase
  • Follows DRY principles by reusing common type definitions

This aligns well with the broader goal of standardizing permission and role representations across the API.

Also applies to: 826-826, 1026-1026, 1129-1129, 1182-1182


1486-1500: Smart field naming for flexible delete and get operations.

The change from specific field names (e.g., PermissionId, RoleId) to generic names (Permission, Role) is well thought out:

  • Intuitive naming that reflects the dual support for both IDs and slugs/names
  • Consistent pattern across all permission and role operations
  • Clear documentation about the flexibility and consequences of operations
  • Proper warnings about the irreversible nature of delete operations

This naming convention better reflects the actual functionality and improves the developer experience by being more intuitive about what values are accepted.

Also applies to: 1514-1527, 1541-1542, 1561-1566

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: 2

♻️ Duplicate comments (11)
go/apps/api/openapi/openapi-generated.yaml (11)

2365-2374: Permission.id pattern still too lax

Previous review noted lack of perm_ prefix enforcement; remains unchanged.


2425-2432: role.id still lacks validation prefix – see prior comment.


541-545: Description still claims “slug” only – but IDs are accepted

The surrounding docs (line 533+) and PR objective state that either a slug or perm_* ID is valid, yet the items.description says “Specify the permission by its slug.”

-                        description: Specify the permission by its slug.
+                        description: Specify the permission by its slug **or** its `perm_*` ID.

584-588: Regex / length block duplicated – extract RoleIdentifier

The exact type/min/max/pattern/description bundle for roles appears in multiple request bodies. Factor it out into a components/schemas/RoleIdentifier and $ref it everywhere to de-duplicate.


875-883: Same “slug only” wording issue as add-permissions

Update the description to reflect slug or ID, otherwise clients will think IDs aren’t allowed.

-                        description: Specify the permission by its slug.
+                        description: Specify the permission by its slug **or** its `perm_*` ID.

965-971: Out-of-date description again

Same wording mismatch – needs “slug or ID”.


1010-1014: Consider re-using RoleIdentifier here as well


1192-1195: Permission regex duplicated across spec – see earlier comment on introducing a reusable PermissionIdentifier schema.


2119-2122: Docs still say “permission slugs” only – mirror earlier fixes.


1501-1506: getPermission claims ID must start with perm_ but regex doesn’t enforce it

To prevent malformed identifiers, tighten the pattern:

-                    pattern: "^[a-zA-Z][a-zA-Z0-9._-]*$"
+                    pattern: "^perm_[a-zA-Z0-9_]+$"

1424-1435: Delete-permission endpoint accepts slug and ID but regex blocks ‘:*’ & ‘.’

The pattern ^[a-zA-Z][a-zA-Z0-9._-]*$ disallows : and *, so callers cannot delete wildcard permissions (documents:*) they previously created. It also diverges from the add/remove/set endpoints which use ^[a-zA-Z0-9_:\\-\\.\\*]+$.

-                    pattern: "^[a-zA-Z][a-zA-Z0-9._-]*$"
+                    pattern: "^[a-zA-Z0-9_:\\-\\.\\*]+$"
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 36eb06f and f237276.

📒 Files selected for processing (3)
  • go/apps/api/openapi/gen.go (19 hunks)
  • go/apps/api/openapi/openapi-generated.yaml (22 hunks)
  • go/pkg/db/types/null_string.go (1 hunks)
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: ogzhanolguncu
PR: unkeyed/unkey#3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses `roleId` as the property name for the role identifier, not `id`. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.
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/db/types/null_string.go (1)

Learnt from: Flo4604
PR: #3631
File: go/pkg/db/bulk_keyring_insert.sql.go:23-25
Timestamp: 2025-07-17T14:24:20.403Z
Learning: In go/pkg/db/bulk_keyring_insert.sql.go and similar bulk insert generated files, hardcoded zero values for fields like size_approx and size_last_updated_at are intentional and reflect the original SQL query structure, not missing parameters.

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

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.

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: CR
PR: unkeyed/unkey#0
File: go/deploy/CLAUDE.md:0-0
Timestamp: 2025-07-21T18:05:58.236Z
Learning: Applies to go/deploy/**/*.{go,js,ts,tsx,py,sh,md,txt,json,yaml,yml,ini,env,conf,html,css,scss,xml,c,h,cpp,java,rb,rs,php,pl,sql} : Do not remove AIDEV-*s without explicit human instruction.

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.

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

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.

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.

Learnt from: Flo4604
PR: #3647
File: go/apps/api/openapi/openapi-generated.yaml:3569-3575
Timestamp: 2025-07-22T18:09:41.800Z
Learning: In the Unkey codebase, using non-standard HTTP status code 529 for internal-only endpoints is acceptable and should not be flagged as an issue in future reviews.

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.

Learnt from: CR
PR: unkeyed/unkey#0
File: go/deploy/CLAUDE.md:0-0
Timestamp: 2025-07-21T18:05:58.236Z
Learning: Applies to go/deploy/**/*.{env,sh,yaml,yml,json,conf,ini} : All environment variables MUST follow the format UNKEY_<SERVICE_NAME>_VARNAME.

Learnt from: CR
PR: unkeyed/unkey#0
File: go/deploy/CLAUDE.md:0-0
Timestamp: 2025-07-21T18:05:58.236Z
Learning: Applies to go/deploy/**/*.{go,js,ts,tsx,py,sh,md,txt,json,yaml,yml,ini,env,conf,html,css,scss,xml,c,h,cpp,java,rb,rs,php,pl,sql} : 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.

go/apps/api/openapi/openapi-generated.yaml (16)

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.

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
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: #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: #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: #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: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

Learnt from: CR
PR: unkeyed/unkey#0
File: go/deploy/CLAUDE.md:0-0
Timestamp: 2025-07-21T18:05:58.236Z
Learning: Applies to go/deploy/**/*.{go,js,ts,tsx,py,sh,md,txt,json,yaml,yml,ini,env,conf,html,css,scss,xml,c,h,cpp,java,rb,rs,php,pl,sql} : Do not remove AIDEV-*s without explicit human instruction.

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

Learnt from: CR
PR: unkeyed/unkey#0
File: go/deploy/CLAUDE.md:0-0
Timestamp: 2025-07-21T18:05:58.236Z
Learning: Applies to go/deploy/**/*.{go,js,ts,tsx,py,sh,md,txt,json,yaml,yml,ini,env,conf,html,css,scss,xml,c,h,cpp,java,rb,rs,php,pl,sql} : 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.

Learnt from: MichaelUnkey
PR: #3173
File: apps/docs/security/delete-protection.mdx:32-36
Timestamp: 2025-04-22T17:33:28.162Z
Learning: In the Unkey dashboard UI for delete protection, the button/link to initiate the process is labeled "Disable Delete Protection" while the confirmation button is labeled "Disable API Delete Protection". The documentation should maintain these different labels to match the actual UI.

Learnt from: MichaelUnkey
PR: #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: ogzhanolguncu
PR: #3661
File: go/apps/api/routes/v2_identities_update_identity/handler.go:115-119
Timestamp: 2025-07-28T11:47:43.144Z
Learning: The v2 update identity endpoint (go/apps/api/routes/v2_identities_update_identity/handler.go) intentionally uses ExternalId field instead of the unified Identity field used in other v2 identity endpoints. This is because the update endpoint needs to both find by externalId and potentially update the externalId value, making the specific field name more appropriate than the generic Identity field.

Learnt from: chronark
PR: #2146
File: apps/dashboard/lib/trpc/routers/api/setDefaultPrefix.ts:80-80
Timestamp: 2024-10-04T17:27:08.666Z
Learning: Ensure that audit log descriptions accurately reflect the action being performed, such as updating the defaultPrefix, and avoid incorrect references like 'name' when not applicable.

Learnt from: MichaelUnkey
PR: #3173
File: apps/docs/security/delete-protection.mdx:21-24
Timestamp: 2025-04-22T17:34:04.438Z
Learning: In the Unkey dashboard UI for enabling delete protection, the button/link to initiate the process is labeled "Enable Delete Protection" while the confirmation button is labeled "Enable API Delete Protection". The documentation should maintain these different labels to match the actual UI.

Learnt from: ogzhanolguncu
PR: #3324
File: apps/dashboard/app/(app)/authorization/roles/components/table/components/actions/keys-table-action.popover.constants.tsx:17-18
Timestamp: 2025-06-19T11:48:05.070Z
Learning: In the authorization roles refactor, the RoleBasic type uses roleId as the property name for the role identifier, not id. This is consistent throughout the codebase in apps/dashboard/lib/trpc/routers/authorization/roles/query.ts.

Learnt from: AkshayBandi027
PR: #2215
File: apps/dashboard/app/(app)/@breadcrumb/authorization/roles/[roleId]/page.tsx:28-29
Timestamp: 2024-10-08T15:33:04.290Z
Learning: In authorization/roles/[roleId]/update-role.tsx, the tag role-${role.id} is revalidated after updating a role to ensure that the caching mechanism is properly handled for roles.

⏰ 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: Build / Build
  • GitHub Check: Test API / API Test Local
  • GitHub Check: Test Packages / Test
  • GitHub Check: autofix
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (10)
go/pkg/db/types/null_string.go (3)

12-19: MarshalJSON implementation looks good!

The method correctly handles both null and valid string cases, using proper JSON marshaling for the string value.


21-35: UnmarshalJSON correctly addresses the previous bug!

The implementation now properly unmarshals JSON strings using json.Unmarshal, which correctly handles quoted strings and escape sequences. This fixes the issue identified in previous reviews.


37-45: SQL interface implementations are correct!

Both Scan and Value methods properly delegate to the embedded sql.NullString type, ensuring compatibility with database operations.

go/apps/api/openapi/openapi-generated.yaml (2)

179-184: EmptyResponse wiring looks good

The new unified empty envelope is correctly referenced here – this promotes consistent 2-tuple {meta,data} responses across the spec.


2000-2003: EmptyResponse component added – good abstraction

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

123-124: LGTM: Clean introduction of standardized empty response type

The EmptyResponse type alias provides a consistent way to represent empty success responses across the API, replacing multiple ad-hoc empty response implementations. This aligns well with the API standardization effort mentioned in the AI summary.


503-504: LGTM: Consistent adoption of EmptyResponse across API endpoints

The migration to use EmptyResponse for delete and update operations creates a uniform response pattern. This improves API consistency and makes it easier for clients to handle empty success responses predictably.

Also applies to: 939-940, 1248-1249, 1505-1506, 1532-1533


737-739: LGTM: Simplified permission handling with auto-creation capability

The documentation correctly reflects the new feature allowing either permission IDs or slugs, with automatic permission creation when the root key has appropriate permissions. The simplified []string format makes the API more intuitive while maintaining backward compatibility.

Also applies to: 978-983, 1070-1080


772-772: LGTM: Unified Permission and Role struct usage

The migration from inline type definitions to proper Permission and Role structs provides better type consistency and makes the response data more structured. This aligns with the standardization effort described in the AI summary.

Also applies to: 822-822, 1010-1010, 1113-1113, 1166-1166


1486-1500: LGTM: Enhanced field semantics for ID/slug flexibility

The field name changes from PermissionId/RoleId to Permission/Role with updated documentation clearly communicate the new capability to accept either IDs or slugs. The comprehensive documentation helps developers understand the flexibility and implications of each approach.

Also applies to: 1514-1527, 1541-1542, 1561-1566

@Flo4604 Flo4604 added this pull request to the merge queue Jul 28, 2025
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Jul 28, 2025
@chronark chronark merged commit 5693e6f into main Jul 28, 2025
18 of 19 checks passed
@chronark chronark deleted the feat-allow-for-permission-slug+id branch July 28, 2025 15:54
@coderabbitai coderabbitai bot mentioned this pull request Jul 29, 2025
18 tasks
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.

2 participants