Skip to content

bug(cloud): admin API HumanUser/ProjectGrant JSON responses use PascalCase, inconsistent with adminTokenMetadata #598

Description

@thomaspufahl

📋 Pre-flight Checks

  • I have searched existing issues and this is not a duplicate
  • I understand this issue needs status:approved before a PR can be opened

📝 Bug Description

The /admin/* JSON API is inconsistent: adminTokenMetadata (served by the token endpoints) already carries snake_case json: tags (principal_id, token_prefix, created_at, ...), but HumanUser and ProjectGrant (served by GET /admin/users and GET /admin/users/{id}/grants) have no json: tags at all, so they serialize with Go-default PascalCase field names (PrincipalID, DisplayName, GrantedByPrincipalID, ...).

This breaks the stable, documented snake_case wire contract for programmatic clients hitting the admin API, and is inconsistent with every other admin response shape (including the request bodies, e.g. createAdminUserRequest, which already use snake_case tags).

🔄 Steps to Reproduce

  1. Start engram with an admin-enabled cloud server.
  2. curl -H "Authorization: Bearer <admin-token>" http://localhost:PORT/admin/users
  3. Inspect the JSON body.

✅ Expected Behavior

GET /admin/users and GET /admin/users/{id}/grants return snake_case field names, consistent with adminTokenMetadata and with every admin request body, e.g.:

[{"principal_id":"p-...","username":"alice","email":"...","display_name":"Alice","role":"member","enabled":true,"created_at":"..."}]

❌ Actual Behavior

They currently return Go-default PascalCase field names, e.g.:

[{"PrincipalID":"p-...","Username":"alice","Email":"...","DisplayName":"Alice","Role":"member","Enabled":true,"CreatedAt":"..."}]

Source: internal/cloud/cloudstore/identity.goHumanUser (~line 58) and ProjectGrant (~line 97) have no json: tags, while adminTokenMetadata in internal/cloud/cloudserver/admin_handlers.go (~line 65) does.

Operating System

Linux (Other)

Engram Version

v1.19.0

Agent / Client

Other

💡 Additional Context

I have a fix ready (adding snake_case json: tags to both structs matching the adminTokenMetadata convention, plus wire-shape assertions in admin_handlers_test.go) and would like to open a PR once this is approved. Verified no other consumer (dashboard templ components, existing tests) depends on the current PascalCase JSON output — all in-repo consumers use direct Go field access or round-trip json.Unmarshal into the same struct, so this is a non-breaking, additive fix in practice.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions