Skip to content

spec: pin recursion-depth limit for nested validation (RFC-001 §6.4) - #97

Merged
trendvidia merged 1 commit into
mainfrom
spec/062-recursion-depth
Jul 16, 2026
Merged

spec: pin recursion-depth limit for nested validation (RFC-001 §6.4)#97
trendvidia merged 1 commit into
mainfrom
spec/062-recursion-depth

Conversation

@trendvidia

Copy link
Copy Markdown
Owner

Closes #62 (spec item #13). Stacked on #96 (train: #94#95#96 → this); merge in order.

What

  • Normative depth limit: root instance at depth 0; entering any message-typed value (nested field, repeated element, map value) increments by 1; default 64; configurable via EngineConfig.max_recursion_depth (field 6 — earmarked for this in spec: pin project-level engine-config format (RFC-001 §9.4) #95, now real; 0 ⇒ default).
  • At the limit, fail closed as a structured violation: the engine does not descend; it records protowire.depth_exceeded (params: {limit}, path at the stopping field, RULE_KIND_VALIDATE), sets Report.truncated = true, and continues with siblings in collect-all mode.
  • protowire. violation-code namespace reserved for spec-defined violations (§7): protowire.required, protowire.depth_exceeded — formalizing what the spec: pin validation report wire shape in report.proto (RFC-001 §7) #94 golden report fixture already assumed.
  • §13 row 4 resolved; CHANGELOG; 08_engine_config.textproto updated and protoc round-trip re-verified.

Design decisions

  1. Violation, not the issue's error-vs-truncate poles: a hard error throws away the rest of the report (one deep subtree hides every other violation); a silent truncate is fail-open — unvalidated data passing as valid, against the error-over-coercion principle. The synthetic violation keeps the report complete, machine-readable, and fail-closed, and Report.truncated (spec: pin validation report wire shape in report.proto (RFC-001 §7) #94) exists precisely to flag non-exhaustive traversal.
  2. Normative, not implementation-defined: otherwise identical deep instances yield non-equivalent reports across ports, breaking goal 5 and the golden-report contract. Only the enforcement mechanism (stack vs. counter) is implementation-defined.
  3. Default 64, not 100: no single parse-depth alignment exists anyway (C++ protoc caps at 100, protobuf-go at 10,000); 64 is comfortably above real schemas, cheaper to guarantee stack-safe on shallow-stack ports (JS, embedded), and one config line away for anyone who needs more. Also bounds worst-case stack on attacker-controlled deep messages — relevant for the public-API driving use case.

Resolves spec issue #13 (GH #62).

- Normative: root at depth 0, each message-typed value +1 (scalars and
  scalar-collection elements do not count), default 64, configurable via
  EngineConfig.max_recursion_depth (field 6, previously earmarked;
  0 = default).
- At the limit: fail-closed synthetic violation
  (protowire.depth_exceeded, params {limit}, RULE_KIND_VALIDATE), sets
  Report.truncated, continues with siblings — never a silent accept
  (fail-open), never a report-destroying hard error.
- Depth definition, default, and at-limit behavior are normative for
  cross-port report equivalence; enforcement mechanism is
  implementation-defined; per-call SPI overrides allowed.
- §7: 'protowire.' violation-code namespace reserved for spec-defined
  violations (protowire.required, protowire.depth_exceeded).
- §13 row 4 resolved; CHANGELOG updated; 08_engine_config.textproto
  gains max_recursion_depth: 64 (protoc round-trip re-verified).
@trendvidia
trendvidia force-pushed the spec/062-recursion-depth branch from d2e0655 to 0c5d978 Compare July 16, 2026 06:30
@trendvidia
trendvidia merged commit 23f0d8b into main Jul 16, 2026
4 checks passed
@trendvidia
trendvidia deleted the spec/062-recursion-depth branch July 16, 2026 06:35
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.

Recursive message validation depth limits

1 participant