Skip to content

bug: caller-controlled scope IDs grow Runtime caches without bound #1322

Description

@AlexStocks

Describe the bug

Caller-controlled scope_id values permanently populate several process-level dictionaries in a long-running Server.

Preparing context for a previously unseen scope creates and retains:

  • one BuiltinRuntime scope lock;
  • one RelationalContexts PowerContext object;
  • one source lock;
  • one activation lock.

There is no TTL, LRU, maximum scope count, weak-reference lifecycle, or explicit close operation for a single scope. A valid request does not need to persist any Source or Memory before these objects are retained.

RFC 0019 intentionally caches a scoped Runtime composition, but the remote Server now accepts arbitrary scope IDs at the HTTP/MCP boundary. The original cache assumption therefore becomes an unbounded resource path.

Steps to reproduce

At commit a6e0dfe:

  1. Start a fresh SQLite-backed Server with Dashboard, MCP, metrics, and access logging disabled.
  2. Send 250 successful POST /v1/context/prepare requests.
  3. Use a different valid scope_id for each request and the same short query.
  4. Inspect the Runtime and RelationalContexts dictionaries.

Observed:

runtime_scope_locks=250
provider_contexts=250
provider_source_locks=250
provider_activation_locks=250

No Source or Memory entries were written.

Relevant paths:

  • src/powercontext/builtin/runtime/application.py:1197 and 1383-1396
  • src/powercontext/builtin/runtime/relational.py:340-346
  • src/powercontext/builtin/runtime/relational.py:449-500
  • docs/en/rfcs/0019_local_source_memory_runtime.md:161-167

Expected behavior

A long-running Server should have a concurrency-safe, bounded lifecycle for scope-local objects.

Possible acceptance criteria:

  • Define a maximum or idle lifecycle for cached scopes.
  • Do not evict a scope lock while an operation still holds or waits for it.
  • Allow inactive scope contexts to be reclaimed without weakening per-scope write serialization.
  • Expose a bounded metric for active/cached scopes without using scope_id as a metric label.
  • Add a public-boundary regression test showing that many one-off scope IDs do not cause permanent linear growth.

Authentication and rate limiting can reduce abuse but do not replace cache lifecycle management for normal long-lived multi-project use.

Actual behavior

Each new valid scope_id permanently increases multiple dictionaries until the process exits. An authenticated caller, or an unauthenticated caller when the Server is publicly bound, can force continued process-memory growth with one-off scope IDs.

Environment

  • PowerContext: 0.0.3.dev11+ga6e0dfe8b.d20260823
  • Commit: a6e0dfe
  • Python: 3.14.3
  • Backend: SQLite
  • OS: Windows 11
  • Probe requests: 250 successful public HTTP prepare_context operations

Are you willing to submit a PR to fix this bug?

  • Yes, I would like to submit a PR.

Activity

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

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions