Skip to content

perf: agentRuntimeTokens.tokenHash is unindexed on the agent-auth hot path #1303

Description

@lilyshen0722

middleware/agentRuntimeAuth.ts:62 and services/agentWebSocketService.ts:308 both resolve an agent's identity with

User.findOne({ 'agentRuntimeTokens.tokenHash': tokenHash, ... })

and there is no index on that path. backend/models/User.ts declares exactly two userSchema.index(...) calls (:365 authProviders, :366 the compound unique) plus field-level index: true on two entitlements.* keys. Nothing covers agentRuntimeTokens.tokenHash.

That query is on the hot path: it runs on every /api/agents/runtime/* request and on every agent WebSocket connect, so each one is a collection scan of User. lastUsedAt is then written back through the same unindexed predicate (:78, and agentWebSocketService.ts:322), so it is two scans per authenticated agent call, not one.

Fix is one line in the schema:

userSchema.index({ 'agentRuntimeTokens.tokenHash': 1 }, { sparse: true });

sparse matters here — most User rows carry agentRuntimeTokens: [] and have no value at that path, so a sparse multikey index only stores entries for rows that actually hold a token.

Scale caveat, stated rather than left to be inferred: at the current user count a scan is not a visible latency problem, and I have not measured one. This is filed as a correctness-of-shape issue on a path whose cost grows with total users while its working set grows with agents only.

One thing I am explicitly NOT asking for

I previously suggested this array should also get select: false, by symmetry with apiToken. That reasoning was wrong and I am withdrawing it:

  • apiToken earns select: false (models/User.ts:234) because it is a plaintext cm_ bearer — an incidental findById() leaks a live credential.
  • agentRuntimeTokens[] stores only tokenHash. agentRuntimeAuth.ts:67 matches on the hash; the plaintext is never persisted.
  • The array is already stripped from responses by controllers/userController.ts:16 (SECRET_USER_FIELDS), with a regression test at __tests__/unit/controllers/userController.secretLeak.test.js:95.

select: false would still be defence in depth against a future route that returns a user without going through that controller, but the precedent I cited for it does not apply, and adding it means auditing every .select('agentRuntimeTokens...') call site (there are ~10) for a now-required +. Not obviously worth it; someone else's call.

Raised in the sprint pod at message 59725; filed here so it is durable somewhere other than pod prose.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions