summaryrefslogtreecommitdiff
path: root/src/main.zig
diff options
context:
space:
mode:
authort <t@tjp.lol>2026-07-01 15:36:20 -0600
committert <t@tjp.lol>2026-07-01 15:37:04 -0600
commit800b2c4b6115845f6bf15d90484b3e80e81a606f (patch)
tree914981ad5c49e8941280d015923b7c3143463d64 /src/main.zig
parentef7c37c3423ceb896f20676525307f15d6e927b4 (diff)
Load extensions via unified policy, entry/activate, rocks and paths
Replace the split [tools]/[extensions] config sections and the two-stage load gate with a single model: - [extensions] is now one policy resolved across all layers. Rules from every layer are kept (not clobbered) and resolved by last-match-wins after sorting by (layer, glob specificity, deny-last), so a base layer can carve one name out of an otherwise-denied group and a higher layer's broad rule still wins. Default is allow; whitelist via deny=["**"]. - Registration is deferred: a source returns an entry {name, activate} (or a list, or the sugar tool table), is always eval'd side-effect-free, and only permitted names get activate()d. Identity is the declared name, not the filename. Collapses the old pre/post-load two-stage gate. - The loader runs two passes (eval -> shadow -> filter -> activate) with precedence project>user>base and, within a layer, rocks<paths<dir. - extensions.paths adds extra scan dirs; extensions.rocks loads luarocks packages as extension sources (require-as-entries). Startup installs only missing rocks (no per-launch network hit); panto update force- (re)installs every configured rock. deny is a feature toggle, not a security boundary: rocks is where registry code enters, so the pin list is the trust boundary. Rock integrity (first-party GPG signing + --verify) is designed but not yet wired; until then rocks pins which version, not which bytes. Breaking: [tools] is removed; extensions must return entries. Built-in tools and the wc example keep working via the sugar tool form.
Diffstat (limited to 'src/main.zig')
-rw-r--r--src/main.zig18
1 files changed, 14 insertions, 4 deletions
diff --git a/src/main.zig b/src/main.zig
index 2c5b414..86f1cb1 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -388,6 +388,17 @@ pub fn main(init: std.process.Init) !void {
// chance to register tools that might want to yield.
try rt.installScheduler();
+ // Install any user rocks (`extensions.rocks`) into the shared tree so the
+ // loader can `require` them below. Best-effort: a rock that fails to
+ // install is logged and skipped — the REPL still starts. This is where
+ // registry code enters, so it is the code-execution trust boundary; the
+ // allow/deny policy is a feature toggle, not a security gate.
+ for (app_config.ext_rocks) |spec| {
+ _ = luarocks_runtime.installRockIfMissing(luarocks_rt, alloc, io, spec.value) catch |err| {
+ std.log.err("extensions.rocks: failed to install '{s}': {t}", .{ spec.value, err });
+ };
+ }
+
// Discover Lua extensions across three layers — base
// (<data home>/agent), user ($XDG_CONFIG_HOME/panto or
// $HOME/.config/panto), and project (./.panto). Project shadows
@@ -399,10 +410,9 @@ pub fn main(init: std.process.Init) !void {
init.environ_map,
luarocks_rt.layout.agent_dir,
rt,
- .{
- .extensions = &app_config.extensions,
- .tools = &app_config.tools,
- },
+ &app_config.extensions,
+ app_config.ext_paths,
+ app_config.ext_rocks,
) catch |err| {
std.log.err("extension discovery failed: {t}", .{err});
return err;