From bf616e192a31e234ca8135b5f4a461ce7057ad3e Mon Sep 17 00:00:00 2001 From: Dai Ha Date: Mon, 17 Aug 2026 16:08:20 +0200 Subject: [PATCH] CB-610: document subscription:, the knob that bills the operator's Claude plan profiles..subscription is read in five places (BridgedConfig record + isSubscription + validateSubscriptionProfiles, ClaudeCodeLauncher, and the startup secret check that deliberately skips it) and was in bridged.example.yaml nowhere. bridged.yaml is gitignored, so the example is the only place an operator can learn a key exists - which meant a fresh host had no way to discover the one switch that moves cost onto the operator's own subscription. Two profiles here have it set. Documents what changes when it is true, that it is mutually exclusive with baseUrl and refused at load, that the startup secret check skips such profiles so a clean secrets report says nothing about them, and that maxLoad is the only throttle against the operator's plan - there is no metering or budget refusal. 94 config tests pass; the example still loads. --- bridged/bridged.example.yaml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/bridged/bridged.example.yaml b/bridged/bridged.example.yaml index 405c903..c1d962b 100644 --- a/bridged/bridged.example.yaml +++ b/bridged/bridged.example.yaml @@ -208,6 +208,29 @@ profiles: # `bridge_spawn{profile:"gx10"}` against it is refused too; a cap holds even when the profile # is named directly. Negative is refused at config load — there is no sane meaning for it. maxLoad: 2 + # subscription: true + # THE KNOB THAT DECIDES WHO PAYS (CB-539). Default false. When true, this profile's members + # run on the OPERATOR'S OWN Claude subscription instead of a metered endpoint — every spawn + # bills your plan and eats your usage limit. Off-subscription is the whole point of this + # daemon, so treat `true` as a deliberate exception, not a convenience. + # + # What changes when it is set (ClaudeCodeLauncher): + # - no ANTHROPIC_BASE_URL and no ANTHROPIC_AUTH_TOKEN are injected — the member inherits + # the operator's own Claude Code auth, which is exactly why it bills the plan; + # - SubscriptionGuard never vets it, because there is no baseUrl to vet; + # - no token is required, so `tokenEnv` is irrelevant here. + # + # MUTUALLY EXCLUSIVE with `baseUrl` — setting both is refused at config load (CB-542). On the + # subscription path no guard would vet the URL, so allowing both would be a way around the + # guard rather than a configuration. + # + # GOTCHA 1 — it is invisible to the startup secret check. `Bridged.reportRequiredSecrets` + # skips subscription profiles on purpose (they need no token), so a boot log that reports + # every secret as fine says nothing about these profiles. + # + # GOTCHA 2 — `maxLoad` is the ONLY throttle you have here. There is no metering, no budget + # and no refusal on cost; the cap on live members is the single thing standing between a + # fan-out and your monthly limit. Set it deliberately and keep it small. # gitTokenEnv: GITEA_TOKEN # opt-in: let this profile's workers open their own PR (CB-302) # gitHostEnv: GITEA_HOST # defaults to GITEA_HOST; injected only with gitTokenEnv # exhaustedPattern: "usage limit has been reached" # opt-in: classify a usage-limit refusal (CB-578)