7.7 KiB
G-003 Configurable resource limits and sandboxing on subshell interps
Status: proposed
Scope: src/modules/punk/repl-999999.0a1.0.tm, src/modules/punk/repl/codethread-999999.0a1.0.tm
Goal: a subshell's code interp can be launched with configurable resource limits (command-count, time) and sandboxing features, building on the resolved first-subshell asymmetry from G-002.
Acceptance: a subshell can be launched with at least one resource limit (command-count via interp limit -command, or time via interp limit -time) and one sandboxing feature (e.g. interp hide of a command, or full safe-interp restrictions) applied to its code interp, enforceable regardless of subshell nesting depth; a subshell can be configured anywhere on the spectrum from unrestricted to fully safe via expose/hide of commands; the existing default subshell behaviour (no limits, no extra sandbox beyond the existing safe/safebase/punksafe types) is preserved when no limits are configured.
Context
The subshell's user code runs in a child Tcl interpreter named code (created in repl::init at src/modules/punk/repl-999999.0a1.0.tm:3575-3620). The existing TODO at repl:3130-3132 is explicit that resolving the "first subshell asymmetry" is a prerequisite for this goal:
"This will be important later for us to control aspects of the code interp such as cpu/memory resource limits and sandboxing."
Tcl provides the primitives this goal needs:
- Resource limits:
interp limitsupports two dimensions — command-count (-command, restricts the total number of Tcl commands that may be executed in the interp) and time (-seconds/-millis). Both are nestable per-interp. These are the only interp-level resource limits Tcl exposes; this goal covers exactly those two. - Sandboxing spectrum: Tcl supports a continuum from unrestricted to fully sandboxed via
interp hide/interp expose/interp alias. At one end, a plaininterp createis unrestricted. At the other,safe::interpCreateproduces a fully safe interpreter with hidden dangerous commands and restricted file/path access. In between,interp hide <command>removes specific commands,interp expose <command>brings them back, andinterp aliascan redirect them to restricted wrappers — so a subshell can be configured to any point on the spectrum. The existingsubshell safe/safebase/punksafeensemble procs (repl-999999.0a1.0.tm:3530-3560) already create safe interps; this goal generalises that to be configurable on thepunk-type subshell too, and exposes the in-between sandboxing points.
The goal is downstream of G-002 because:
- The "first subshell asymmetry" fix (G-002) ensures all subshells — including the first — run through the same
code-interp creation path, so limits/sandbox applied at creation time are uniform regardless of nesting depth. - G-002's canonical launch API design (section 0 of G-002's approach) makes the dispatch — running in the parent via
interp alias— the single owner ofcode-interp creation. This is the property that makes limits/sandbox uniform: the dispatch applies-limits/-sandboxat creation time for every launch, at every nesting depth, because there is no other code path that creates acodeinterp. - Resource limits are only meaningful if the subshell actually runs code in the
codeinterp; if the asymmetry persists, the first subshell's limits would need a separate code path, defeating the goal.
The existing safe-interp variants already prove the sandboxing primitive works; this goal is about making it configurable and adding resource limits, not inventing new sandboxing technology.
Approach
-
Limit configuration surface. Add a
-limitsoption to the subshell launch (and torepl::initwhere thecodeinterp is created) accepting a dict of limit specs:{command <count>}for command-count and{time <seconds>}for time limits (millisecond granularity to be decided during implementation). Apply viainterp limit code ...immediately afterinterp create code. -
Sandbox configuration surface. Add a
-sandboxoption accepting a spec that places the subshell anywhere on the spectrum from unrestricted to fully safe. Mechanisms:interp hide <command>to remove specific commands,interp expose <command>to bring them back,interp aliasto redirect to restricted wrappers, andsafe::interpCreatefor the fully-safe endpoint. The existingsafe/safebase/punksafelaunch types become presets that set-sandboxto their respective defaults, proving the general mechanism covers the existing cases. -
Nesting-depth independence. Apply limits/sandbox at
code-interp creation time, which (after G-002's asymmetry fix) is uniform for all nesting depths. Confirm that nested subshells inherit or re-apply the configured limits rather than silently dropping them — the exact inheritance rule (do limits cascade to nestedcodeinterps, or must each level specify its own?) is to be decided during implementation and documented in the subshell command's help text. -
Default preserved. When neither
-limitsnor-sandboxis specified, the subshell launches with no limits and no extra sandbox beyond what the existing launch type already applies (e.g.punktype = no sandbox;safetype = full safe sandbox). This preserves backward compatibility.
Alternatives considered
- Always-safe subshells, drop the
punktype. Rejected: thepunk-type subshell exists for trusted code that needs full Tcl access. Sandboxing should be opt-in, not mandatory. - OS-level limits only (process/thread, not interp). Rejected:
interp limitcovers command-count and time at the interp level, which is finer-grained and doesn't require process boundaries. These two dimensions are what Tcl exposes; pursuing OS-level limits (e.g. memory) would be a separate goal if ever needed. - A new launch type (e.g.
subshell limited) instead of options on existing types. Rejected: options compose better than proliferating launch types. A user should be able to saysubshell punk -limits {command 100000}without choosing a different subcommand. - Fold this into G-002. Considered and rejected: resource limits + sandboxing is a distinct deliverable with its own acceptance, and the existing code comment at repl:3130-3132 already frames it as "important later" — explicitly downstream. Bundling it into G-002 would make G-002's acceptance uncheckable until this work is also done, and would grow G-002 past a one-line-safe summary.
Notes
- Depends on G-002's "first subshell asymmetry" fix being complete. Sequence G-002 before G-003.
- The two interp-level resource limits Tcl exposes are command-count (
interp limit -command) and time (interp limit -time). No other interp-level resource limits are in scope; if OS-level limits (e.g. memory via rlimit/job objects) are wanted later, that's a separate goal. - The existing
safe/safebase/punksafeensemble procs (repl-999999.0a1.0.tm:3530-3560) are the natural testbed for the sandboxing configuration surface: they should be reimplemented as presets that set-sandboxto their current behaviour, proving the general mechanism covers the existing cases. - The sandboxing spectrum is a continuum via
interp hide/interp expose/interp alias/safe::interpCreate; the-sandboxoption should expose enough of that continuum to let a user say "hide command X and Y, leave the rest" without choosing a different subcommand. interp limitwith-commandcounts commands in the interp; nested interps may or may not count against the parent's limit depending on Tcl's implementation. Confirm during implementation and document in the subshell help text.- No persisted prior chat on this topic was found in project sessions; the motivation comes from the existing TODO at repl:3130-3132 and the user's stated intent.