# 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 limit` supports 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 plain `interp create` is unrestricted. At the other, `safe::interpCreate` produces a fully safe interpreter with hidden dangerous commands and restricted file/path access. In between, `interp hide ` removes specific commands, `interp expose ` brings them back, and `interp alias` can redirect them to restricted wrappers — so a subshell can be configured to any point on the spectrum. The existing `subshell safe`/`safebase`/`punksafe` ensemble procs (`repl-999999.0a1.0.tm:3530-3560`) already create safe interps; this goal generalises that to be configurable on the `punk`-type subshell too, and exposes the in-between sandboxing points. The goal is downstream of G-002 because: 1. 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. 2. 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 of `code`-interp creation. This is the property that makes limits/sandbox uniform: the dispatch applies `-limits`/`-sandbox` at creation time for every launch, at every nesting depth, because there is no other code path that creates a `code` interp. 2. Resource limits are only meaningful if the subshell actually runs code in the `code` interp; 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 1. **Limit configuration surface.** Add a `-limits` option to the subshell launch (and to `repl::init` where the `code` interp is created) accepting a dict of limit specs: `{command }` for command-count and `{time }` for time limits (millisecond granularity to be decided during implementation). Apply via `interp limit code ...` immediately after `interp create code`. 2. **Sandbox configuration surface.** Add a `-sandbox` option accepting a spec that places the subshell anywhere on the spectrum from unrestricted to fully safe. Mechanisms: `interp hide ` to remove specific commands, `interp expose ` to bring them back, `interp alias` to redirect to restricted wrappers, and `safe::interpCreate` for the fully-safe endpoint. The existing `safe`/`safebase`/`punksafe` launch types become presets that set `-sandbox` to their respective defaults, proving the general mechanism covers the existing cases. 3. **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 nested `code` interps, or must each level specify its own?) is to be decided during implementation and documented in the subshell command's help text. 4. **Default preserved.** When neither `-limits` nor `-sandbox` is specified, the subshell launches with no limits and no extra sandbox beyond what the existing launch type already applies (e.g. `punk` type = no sandbox; `safe` type = full safe sandbox). This preserves backward compatibility. ## Alternatives considered - **Always-safe subshells, drop the `punk` type.** Rejected: the `punk`-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 limit` covers 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 say `subshell 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`/`punksafe` ensemble 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 `-sandbox` to 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 `-sandbox` option 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 limit` with `-command` counts 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.