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
Captured from the incident-95 local-credits thread (2026-08-02). This design position was argued at length by @huntcsg and agreed in principle by @christian-byrne, and until now existed only in Slack. #14591 shipped a deliberately narrow slice of it; this issue records the rest so the direction is not lost.
Problem / Goal
A user's ability to do something is currently derived at each call site by combining several independent signals. Hunter's enumeration of what actually determines a capability today:
subscription tier level
feature flag
default entitlement (possibly a function of tier)
manually overridden entitlement
distribution (cloud, local, desktop)
role
JWT scope
And soon, per-object capabilities: canEdit, canDelete, canExecute on individual resources.
Every call site that recombines these itself is an opportunity to get it wrong. The P0 that started this thread is the worked example: isActiveSubscription is forced true off cloud, so a call site that reasonably read it as "this user has a subscription" produced a dead button on local builds. See also #14862 for three duplicate top-up gates that have since drifted apart.
Hunter's framing of the end state, quoted so it does not get paraphrased away:
I kind of think we want to move to a place where frontend ALWAYS calls the host (cloud.comfy.org or localhost:8188 or whatever), and core api itself can be trusted to respond back with what this particular instance supports.
The complication he raised against a purely backend-authoritative model: the frontend serves multiple genuinely different backends, and comfy-api / the cloud backend is not the local backend. So the resolution point cannot simply be "ask cloud".
Christian's counter-position, which #14591 implements: the combinatorial explosion should be enumerated once in a single module rather than spread across call sites, with app code only ever asking canUseCapability(x). That is code organization, not a change in where configuration lives, and it makes a later move toward API-supplied entitlements a small mechanical refactor rather than a rewrite.
Also unresolved from the same thread, and worth deciding explicitly: should a local-only account be modelled as free tier at all? Today creating an account locally provisions a cloud-backed account which reports subscription_tier: FREE. Deep Mehta confirmed everyone with a cloud account is free tier and that local users get nothing from it. If the semantic answer is "a local-only user is not a free-tier user", the current API shape is a standing source of this bug class.
Captured from the incident-95 local-credits thread (2026-08-02). This design position was argued at length by @huntcsg and agreed in principle by @christian-byrne, and until now existed only in Slack. #14591 shipped a deliberately narrow slice of it; this issue records the rest so the direction is not lost.
Problem / Goal
A user's ability to do something is currently derived at each call site by combining several independent signals. Hunter's enumeration of what actually determines a capability today:
And soon, per-object capabilities:
canEdit,canDelete,canExecuteon individual resources.Every call site that recombines these itself is an opportunity to get it wrong. The P0 that started this thread is the worked example:
isActiveSubscriptionis forcedtrueoff cloud, so a call site that reasonably read it as "this user has a subscription" produced a dead button on local builds. See also #14862 for three duplicate top-up gates that have since drifted apart.Hunter's framing of the end state, quoted so it does not get paraphrased away:
The complication he raised against a purely backend-authoritative model: the frontend serves multiple genuinely different backends, and comfy-api / the cloud backend is not the local backend. So the resolution point cannot simply be "ask cloud".
Christian's counter-position, which #14591 implements: the combinatorial explosion should be enumerated once in a single module rather than spread across call sites, with app code only ever asking
canUseCapability(x). That is code organization, not a change in where configuration lives, and it makes a later move toward API-supplied entitlements a small mechanical refactor rather than a rewrite.Also unresolved from the same thread, and worth deciding explicitly: should a local-only account be modelled as free tier at all? Today creating an account locally provisions a cloud-backed account which reports
subscription_tier: FREE. Deep Mehta confirmed everyone with a cloud account is free tier and that local users get nothing from it. If the semantic answer is "a local-only user is not a free-tier user", the current API shape is a standing source of this bug class.Proposed Solution
Decide between, or sequence, these:
Whichever is chosen needs an answer for per-object capabilities, since those cannot be a single global map.
Acceptance Criteria
docs/adr/) naming the chosen resolution point and why.