nebari-dev / nebari-dev/nebari-frames

Resolved Frame content is unbounded even though stored content is capped

Open
#68 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

type: enhancement 💅🏼
Dominant language
Go
Stars
2
Forks
1
Avg merge
10h 55m
Merged PRs (30d)
9

Description

Problem

#51 implemented the 512KB cap the design doc promised, but it applies to a single version's stored document (frames.MaxContentBytes, checked in the shared publish path). The resolved form is still unbounded: Resolve merges every ancestor's slots, so a Frame that inherits from a chain of near-limit parents can compose to far more than 512KB.

The design doc originally claimed "A Frame with 100MB of inherited content is rejected at publish time, not at MCP-read time." That is still not true - the note in docs/design/2026-05-21-mcp-endpoint-design.md §6 was updated in #51 to say so honestly rather than leave the claim standing.

Why it matters

The resolved form is what actually gets served and held in memory:

  • ResolveDoc walks the whole ancestor graph on every GetFrame, ResolveFrame, get_frame, and MCP resource read.
  • Nothing caches it, so the cost is per request.
  • With replicaCount: 1 and SQLite, a handful of concurrent reads of a deeply-inherited Frame is a memory and latency problem on a single pod.

Inheritance depth is not otherwise limited either - only cycles are rejected (CycleError), not depth or fan-out.

Suggested direction

Options, roughly in order of cost:

  1. Cap resolved size at publish time: resolve the document as part of publish and reject when the composed form exceeds a limit. Catches it at write time, but makes a publish's validity depend on its ancestors, so a parent growing can retroactively invalidate a child's next publish.
  2. Cap at read time: return a clear error rather than serving an enormous payload. Simple, but the failure surfaces to a reader who cannot fix it.
  3. Limit inheritance depth and total ancestor count, which bounds the blast radius without a byte-level rule.

A decision is needed on which limit is the real contract before implementing; that is the substance of this issue.

Context

Raised by review of #51. The per-version cap landed there; this is the remaining half.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with docs/design/2026-05-21-mcp-endpoint-design.md §6 and trace ResolveDoc through publish, GetFrame, ResolveFrame, get_frame, and MCP resource reads. First settle whether the contract is a resolved-byte, depth, or ancestor-count limit; done means the chosen behavior is documented and its affected publish/read paths are covered.

Written by the indexing model from the issue text.

Assessment

Tech stack
go, sqlite
Domain
api, backend, performance
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Needs clarification
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.