Skip to content

quota(opendesign): billing quota ask cannot terminate at the OpenCode server — session-cookie credential the server cannot hold or use #5060

Open

Description

@dezren39

Gap

Drew's ruling (2026-11-03): a quota ask must terminate at the OpenCode server — not at a standalone proxy, and not at a credential store the server never sees. The Meta leg now complies: the quota gatherer (pm-stack/aggregator/scripts/gather-quota.ts) asks the standing fork server's GET /api/credential (127.0.0.1:13420) for the credential, with the direct opencode.db read kept only as a transport-failure fallback (the quota CLI's combined-source shape, #2490).

The OpenDesign leg cannot comply today, and this issue names why precisely:

  1. The OpenChamber quota module's OpenDesign provider (server/lib/quota/providers/opendesign.js in the OpenChamber fork) does not read the OpenCode credential machinery at all. It reads a managed session credential from OpenChamber's own quota credential store (~/.config/openchamber/quota/opendesign.json): a Better Auth session cookie plus a workspace id.
  2. The usage API it answers from (https://amr-api.open-design.ai/api/auth/get-session, .../v1/workspaces/:id/billing/coding-plan-usage) accepts only that session cookie (Cookie: better-auth.session_token=… + x-vela-workspace-id header). Verified live 2026-11-03: the managed cookie returns 200.
  3. The credential the OpenCode server holds for integration opendesign is the AMR inference API key (type: key, sk-a…). Verified live 2026-11-03: that key is refused by the billing/session API — 403 as a Bearer token and 403 presented as the session cookie. It cannot answer the quota read.
  4. The OpenCode fork server exposes no quota/usage surface to terminate the ask at: its live route table (/openapi.json, v2.0.30) has credential, session, integration, provider, etc. groups, but no quota group, and no credential shape that holds a (session cookie, workspace id) pair for a billing API.

So the OpenDesign read currently terminates at OpenDesign's own API with a locally-stored session cookie that the OpenCode server has no access to. The gatherer keeps that read working (fail-soft contract: never zero a pool over a routing principle), but the hop is named here rather than silently left in place.

What closing this takes (any one)

  • The OpenCode fork server gains a quota surface for OpenDesign: e.g. a credential shape that can hold the AMR session cookie + workspace id, plus a server-side usage read (or a credential projection the quota module can consume via GET /api/credential in place of the managed store) — then opendesign.js switches its credential source to the wired OpenCode source like the other providers; or
  • AMR's billing API starts accepting the inference API key the server already holds (then the provider can read the key from the server like Meta does); or
  • A documented decision that OpenDesign is a permanent token-exception provider (cf. audit(quota-plane): name the two token-exception providers definitively #1155), recorded on the board/quota docs so the exception is explicit rather than ambient.

Note the session cookie is short-lived (the current one expires 2026-11-10), so any server-side home for it also needs the re-import/refresh story the OpenChamber Settings import provides today.

Acceptance criteria

  • The OpenDesign quota leg terminates at the OpenCode server like the Meta leg does: the credential the server needs is held/usable by the server, not only in OpenChamber's standalone managed-cookie store — or a recorded decision names this leg as an explicit exception with its reasoning.
  • gather-quota's OpenDesign leg returns current coding-plan usage without depending on ~/.config/openchamber/quota/opendesign.json as its sole source.
  • The direct-store read, if retained at all, is demoted to the same transport-failure-fallback status the Meta leg's opencode.db read has (unify(quota-plane): one quota read path — server endpoint, CLI, and aggregator gatherer must share the direct credential-store read #2490 shape).

Activity

  1. developing-today commented on Nov 3, 2026

    @developing-today
    Contributor

    Premise update (2026-11-03, quota pass-through — PR #5080, merged f508cfb5): the dashboard is now a straight display of the OpenCode quota read. The layers that could drop or override the OpenDesign read are gone (the #1114 disabled-door filter and its exemption hack, the extractor note promotion, the overlay's silent drop of unmatched entries), and the live side-by-side after deploy shows the board rendering the read's opendesign entry exactly as reported (5h 100%, 7d 34.43% at the 19:08Z tick, freshness stamped from the read).

    So the OpenDesign READ is not, and after #5080 cannot again become, a dashboard problem. What remains of this issue is accurately a fork-surface gap, and it stays open for exactly this:

    • The OpenCode server exposes no quota/usage surface and holds no credential of the shape the AMR billing/session API accepts (Better Auth session cookie + workspaceId). The credential the server holds for the opendesign provider is the AMR inference API key, which that billing API refuses (verified 403 as Bearer and as cookie when this issue was filed).
    • The quota read therefore terminates at OpenChamber's managed session credential (in OpenChamber's own quota store) rather than at the server — the one read in the fleet that cannot follow the "ask OpenCode" direction every other pool now uses.
    • That session cookie expires 2026-11-10; when it lapses, the module's OpenDesign read fails until the session is renewed in OpenChamber (the failure will surface honestly on the board as the pool's error/stale freshness — after quota: straight pass-through of the OpenCode quota read (Drew, 2026-11-03) #5080 nothing can silently hide or replace it).

    Closure paths are unchanged from the issue body: (1) the fork grows a quota surface (or a credential shape the AMR billing API accepts), (2) OpenChamber exposes its managed session credential through a sanctioned read, or (3) AMR accepts the inference key for the billing read. Until one lands, this issue tracks a fork/credential-surface gap — not a read gap and not a dashboard gap.

  2. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    Intake review

    — fleet monitor intake pass (repo-fleet-monitor, 2026-11-03 12:12 CST run)

  3. added
    kind/designA design proposal (design-architect): multi-step, phased, decision-bearing
    priority/highImportant and time-sensitive
    effort/mMedium: expected under 1 day of work
    area/infraInfrastructure, deployment, and operations
    meta/enrichedIntake review done: acceptance criteria, links, and normalized labels added
    on Nov 3, 2026
  4. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    agent-note:v1
    state: queued (task 8926)

  5. added
    task/triageQueue packet kind: verify, reproduce, and route this item
    queue/queuedHas a queued task in the Supabase agent queue
    work/assignedQueue worker lifecycle state
    queue/claimedQueue worker lifecycle state
    and removed
    queue/queuedHas a queued task in the Supabase agent queue
    on Nov 3, 2026
  6. dezren39 commented on Nov 4, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · handed off

  7. dezren39 commented on Nov 4, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · picked up

  8. dezren39 commented on Nov 4, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · escalated

    • Agent: queue worker (repo-worker-code), lane 1a180ae5-384f-4e71-8d77-08f7b1652c75 (OpenChamber scheduled lane)
    • Session / ID: task 8926 (queue repo-health-code-pilot)
    • Found via: OpenChamber scheduled lane claim
    • Task: Current-main triage of the OpenDesign quota credential-surface gap.

    Verified current state

    Drew decision required before implementation

    1. Preserve the server-first ruling: approve a scoped OpenCode-owned credential/provider design for {session cookie, workspace id} with provider-only use, no secret disclosure through generic credential APIs, and an explicit manual renewal path (Turnstile sign-in is not automated); then route implementation across OpenCode/OpenChamber.
    2. Approve a permanent exception: keep the current explicitly user-imported, OpenChamber-managed 0600 credential and direct quota fetch; document that this one provider does not terminate at OpenCode, and keep manual renewal as the supported lifecycle.

    Recommendation: do not store the cookie as a normal OpenCode key credential. If the server-first policy is retained, choose option 1 with a separately scoped secret boundary; otherwise record option 2 as an explicit exception. No credential values were read, and no implementation was attempted.

  9. added
    queue/blockedQueue or user escalation state
    status/blockedQueue or user escalation state
    user/escalatedQueue or user escalation state
    and removed
    queue/claimedQueue worker lifecycle state
    on Nov 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area/infraInfrastructure, deployment, and operationseffort/mMedium: expected under 1 day of workkind/designA design proposal (design-architect): multi-step, phased, decision-bearingmeta/enrichedIntake review done: acceptance criteria, links, and normalized labels addedpriority/highImportant and time-sensitivequeue/blockedQueue or user escalation statestatus/blockedQueue or user escalation statetask/triageQueue packet kind: verify, reproduce, and route this itemuser/escalatedQueue or user escalation statework/assignedQueue worker lifecycle state

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions