Summary
A sharing rule created at runtime for an object that had no sharing rule at boot never binds its enforcement hook, so it silently does not take effect until the next restart. The rule definitions are read live (enforcement is correct once a hook fires) — the gap is that hook binding is boot-only, and the create trigger is a data write, so the #2576 metadata:reloaded path doesn't even apply here.
Found in the post-#2576 boot-cached-consumer audit. NOTE: not yet runtime-verified (code analysis; high confidence) — worth a quick repro before fixing.
Evidence
packages/plugins/plugin-sharing/src/sharing-plugin.ts:201 — bindRuleHooks(engine, ruleService, rules, ...) is called once at boot (kernel:ready) with the rules that existed then. It registers insert/update hooks only for the objects that had a rule at that moment.
- Rule evaluation reads rules live from
sys_sharing_rule on each hook fire (sharing-rule-service.ts — getRule/evaluateAllForRecord), so enforcement itself is not stale.
- But sharing rules are data rows (
sys_sharing_rule), authored via a data write (defineRule / POST /data/sys_sharing_rule), not a metadata publish. So metadata:reloaded never fires for a new rule — the flow-pattern rebind wouldn't trigger.
- Result: create the first sharing rule for object
X at runtime → no hook was ever bound for X → the rule never evaluates until restart. (Rules for objects that already had ≥1 rule at boot are fine.)
Impact
priority:p2, security — access-control correctness: an admin adds a record-sharing rule expecting it to restrict/grant visibility, and it silently no-ops until a reboot. Narrow (only the object-had-no-rule-at-boot edge) but security-relevant.
Proposed fix
Give the sharing plugin a data-change trigger on sys_sharing_rule (after insert/update/delete) that re-runs the bind: unbind all rule hooks + bindRuleHooks with the fresh listRules() (mirrors boot; enforcement already reads live so no teardown-of-definitions concern). This is a different mechanism from metadata:reloaded because the source is data, not metadata. Verify with a member-user E2E (create a rule at runtime for a fresh object → sharing takes effect without restart).
Companion to the hooks (#2588) and i18n runtime-authoring gaps.
Summary
A sharing rule created at runtime for an object that had no sharing rule at boot never binds its enforcement hook, so it silently does not take effect until the next restart. The rule definitions are read live (enforcement is correct once a hook fires) — the gap is that hook binding is boot-only, and the create trigger is a data write, so the #2576
metadata:reloadedpath doesn't even apply here.Found in the post-#2576 boot-cached-consumer audit. NOTE: not yet runtime-verified (code analysis; high confidence) — worth a quick repro before fixing.
Evidence
packages/plugins/plugin-sharing/src/sharing-plugin.ts:201—bindRuleHooks(engine, ruleService, rules, ...)is called once at boot (kernel:ready) with the rules that existed then. It registers insert/update hooks only for the objects that had a rule at that moment.sys_sharing_ruleon each hook fire (sharing-rule-service.ts—getRule/evaluateAllForRecord), so enforcement itself is not stale.sys_sharing_rule), authored via a data write (defineRule/POST /data/sys_sharing_rule), not a metadata publish. Sometadata:reloadednever fires for a new rule — the flow-pattern rebind wouldn't trigger.Xat runtime → no hook was ever bound forX→ the rule never evaluates until restart. (Rules for objects that already had ≥1 rule at boot are fine.)Impact
priority:p2,security— access-control correctness: an admin adds a record-sharing rule expecting it to restrict/grant visibility, and it silently no-ops until a reboot. Narrow (only the object-had-no-rule-at-boot edge) but security-relevant.Proposed fix
Give the sharing plugin a data-change trigger on
sys_sharing_rule(after insert/update/delete) that re-runs the bind: unbind all rule hooks +bindRuleHookswith the freshlistRules()(mirrors boot; enforcement already reads live so no teardown-of-definitions concern). This is a different mechanism frommetadata:reloadedbecause the source is data, not metadata. Verify with a member-user E2E (create a rule at runtime for a fresh object → sharing takes effect without restart).Companion to the hooks (#2588) and i18n runtime-authoring gaps.