You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
feat(permissions): lockCaller that pins the CALLING module from inside a service leaf that accepts a callback #477
self.slothlet.lockCaller(fn) captures getCallerIdentity().currentWrapper. A service leaf that accepts a callback — e.g. a host scheduler leaf every(interval, fn) mounted via object-form api.add — can only pin itself with it: inside the leaf, the current wrapper is the leaf, and the module that called the leaf is callerWrapper. So callbacks handed to service leaves run as the service, and identity-scoped calls inside them (self.store.open-style partitioning, permission rules keyed by caller, surface grants) bind the service's identity instead of the calling module's.
Today every consumer must remember to pass self.slothlet.lockCaller(fn) itself, which is easy to miss and fails quietly (the callback just runs as the wrong module).
Proposal
A variant that captures the caller of the current leaf rather than the leaf itself, e.g. self.slothlet.lockCaller(fn, { to: "caller" }) or self.slothlet.lockCaller.caller(fn), resolving the identity the same way metadata.caller() does (callerWrapper). Permission-gated like lockCaller (built-in allowed). The same gap applies to event.on / hook.on when a service leaf subscribes on a caller's behalf — they could accept the same option.
Downstream
@cldmv/rummage ext-host: command handlers and event listeners are now pinned at registration via lockCaller (correct), but host service leaves that take callbacks (self.host.scheduler.every/once) cannot pin the calling extension and currently document "pass self.slothlet.lockCaller(fn)".
Folding in #489, which asks for the same capability from another consumer. Requirements it adds on top of this issue:
Permission-gated: letting a leaf act as its caller is a privilege, so it needs an explicit grant, the same kind of rule as metadata.caller, rather than being allowed by default like plain lockCaller.
Immutable after capture: the identity is captured at call time and can't be changed afterwards, with the same guarantees as today's lockCaller.
Second real case: rummage's deadline extension builds a reminder engine per consumer via self.deadline.engine(...) and ticks them all from one scheduled job. The ticks run as the deadline extension, so owner attribution is carried as a payload field (owner, from metadata.caller()) instead of by caller identity.
Option name is still open: #489 proposed lockCaller(fn, { as: "caller" }); this issue proposed { to: "caller" } or lockCaller.caller(fn).
Landed on next in #516 (067a8229) as self.slothlet.lockCaller.caller(fn), gated by a built-in deny the host grants. Ships in 3.22.0; this closes when it reaches master.
Activity
Shinrai commentedon Sep 28, 2026
Folding in #489, which asks for the same capability from another consumer. Requirements it adds on top of this issue:
metadata.caller, rather than being allowed by default like plainlockCaller.lockCaller.self.deadline.engine(...)and ticks them all from one scheduled job. The ticks run as the deadline extension, so owner attribution is carried as a payload field (owner, frommetadata.caller()) instead of by caller identity.Option name is still open: #489 proposed
lockCaller(fn, { as: "caller" }); this issue proposed{ to: "caller" }orlockCaller.caller(fn).Shinrai commentedon Sep 29, 2026
Landed on
nextin #516 (067a8229) asself.slothlet.lockCaller.caller(fn), gated by a built-in deny the host grants. Ships in 3.22.0; this closes when it reachesmaster.release: v3.22.0 - type the composed api, self, and slothlet() from e…
cldmv-bot commentedon Oct 3, 2026
Closed in v3.22.0 — resolved by #516.