Trim applyToken SigningCondition fan-out once override layer is rarely-consulted #12
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Context
Filed as a parking-spot follow-up to #11 (live-policy auth + companion admin RPCs). Surfaced in webapp's review of the #11 implementation plan (2026-05-30 — see comment 1473 and the coord-log entry that follows it).
Today
After #11 lands, sign-time authorization works in two layers:
SigningConditionrows scoped to aKeyUser. Consulted first incheckIfPubkeyAllowed. Used for per-user grants beyond the policy and per-user denies that override the policy.KeyUser → Token → Policy → PolicyRule. Consulted only if no matchingSigningConditionrow exists for the (method, kind) being checked.The
applyTokenhelper (src/daemon/backend/index.ts:77-121) still fans outSigningConditionrows at token-bind time — one perPolicyRuleon the boundPolicy. That fan-out is what kept legacy auth working before #11; under #11 it becomes redundant for the happy path, because the live-policy join would have produced the same result.Why this isn't urgent
The fan-out is not wrong under #11 — it's a pre-population of the override layer that just happens to mirror the base layer. Auth answers are identical either way. The two costs of leaving it in place:
SigningConditionrows perKeyUserinstead of zero. Each row is tiny (~50 bytes), so a 1000-user bunker holds ~tens of KB of redundant data. Not a real cost yet.PolicyRulemutation (e.g.remove_policy_rulevia #11's new RPC) needs to be reflected per-user, the live-policy path handles it automatically but the pre-populated SigningCondition row would still claim "allowed" via step 3 of the auth check. Concretely: an operator removes a kind from the sharedlnbits-defaultpolicy expecting global revocation, but every KeyUser still holds a SigningCondition row granting that kind, so the live-revoke headline feature of #11 silently doesn't fire.The second is the real reason this should be trimmed eventually — it actively undermines the #11 design intent for already-bound users. But it doesn't undermine #11 for new bindings, and operators can be told to "wipe the affected SigningCondition rows manually after a remove_policy_rule" as a transitional admin op.
Proposed work
checkIfPubkeyAllowedthat counts how often step 3 (override) returns true vs step 4 (live join) returns true. Run for some period on the regtest stack and (ideally) a non-prod environment with real traffic. If step 3 is consulted in >5% of sign requests, that's signal that the override layer is doing real work and trimming the fan-out would change observable behavior.backend/index.ts:96-112that creates a SigningCondition per PolicyRule. Leave only the connect-method row at lines 88-94 (still useful as the "this user has been bound" marker).aiolabs/lnbits#35's reconciliation pass to assume the override layer is no longer pre-populated —lnbits-default'sadd_policy_rulecalls become the only path to permissions for new users.Scope guard
This explicitly does NOT cover trimming the override layer entirely. Override remains useful for:
method='*'allowed=false explicit-reject row).revoke_userbinary path, though that's tracked onKeyUser.revokedAtnot SigningCondition.cc #11 (parent), aiolabs/lnbits#35 (downstream consumer).
Superseded by #27 (merged + deployed). This issue asked to trim the
applyTokenSigningCondition fan-out once the override layer became rarely-consulted — #27 went further and removed it entirely:applyTokennow records only theKeyUser ← Tokenbinding, and token grants are evaluated live via the ACL step-4 token join (Token → Policy → PolicyRule). There is no fan-out left to trim.Closing as done by #27.