Skip to content

[Future] Daily spend limit reads tenant-writable data: move it to a server-side ledger #1344

Open

Description

@frankbria

Spun off from PR #1343 (issue #1303) during post-merge disposition.

Context

CODEFRAME_USER_DAILY_COST_LIMIT_USD (codeframe/core/spend_limit.py) sums today's token_usage from each workspace's .codeframe/state.db. Agent code running in that workspace can write the file, so a tenant can delete rows, or the DB, and lower its own meter. Three related approximations from the same design:

  • Attribution is by workspace (workspace_spend_users), not by caller, so a shared workspace counts in full toward each user who ran in it. This fails closed, but it is inexact.
  • In-flight holds live in process memory, so a multi-worker server under-counts (same caveat as stream tickets, [P1.18] Stop putting long-lived JWTs in SSE/WebSocket URL query strings #745).
  • Delegated engines (claude-code, codex, opencode) are refused while a limit is set, because their spend never reaches token_usage.

Why it was not done in that PR

It needs its own design: a ledger the server owns and children cannot write. That is the same prerequisite as hosted execution (#1266, #1322).

Definition of done

  • Spend that counts toward the limit is recorded in the control-plane DB, keyed by principal, by the server or a channel the agent cannot write.
  • spend_today_usd reads that ledger; deleting a workspace's state.db no longer lowers the meter (test).
  • Holds survive multiple workers (shared storage), or the server refuses to start multi-worker with a limit set.

Activity

  1. frankbria commented on Oct 2, 2026

    @frankbria
    OwnerAuthor

    Gated (needs-owner): this needs an OS-level isolation decision (bubblewrap vs. a separate uid vs. a container per run) that the owner chose to defer on 2026-10-02 in favour of the P1 launch blockers. Hosted execution is already refused (#1266), so this only matters once hosted mode launches.

  2. frankbria commented on Oct 6, 2026

    @frankbria
    OwnerAuthor

    Owner steps (2026-10-06)

    Priority 7 of 7. Deferred, together with #1322: it needs the same isolation decision, so make that choice on #1322 and nothing more here, with one extra sub-decision.

    When hosted mode is scheduled:

    1. Make the #1322 decision (isolation model). See that issue's steps.

    2. Decide how in-flight spend holds behave with several server workers.

    gh issue comment 1344 --repo frankbria/codeframe --body "Decision: holds = <refuse multi-worker | redis>."

    3. The agent then moves spend to a server-side ledger in the control-plane DB, keyed by principal. Its acceptance test: deleting a workspace's state.db no longer lowers the meter. You approve the PR.

  3. added
    FutureDeferred - beyond v1/v2 scope, consider for future versions
    and removed
    needs-ownerGated on an owner decision the agent must not invent
    on Oct 6, 2026
  4. frankbria commented on Oct 6, 2026

    @frankbria
    OwnerAuthor

    Relabelled needs-owner → Future (owner, 2026-10-06). It stays deferred until hosted mode is scheduled; hosted execution is refused until then (#1266). The isolation-model decision steps above still apply when it's picked up.

  5. changed the title [-][P2.71] Daily spend limit reads tenant-writable data: move it to a server-side ledger[/-] [+][Future] Daily spend limit reads tenant-writable data: move it to a server-side ledger[/+] on Oct 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    FutureDeferred - beyond v1/v2 scope, consider for future versionsP2-medium-betaMedium priority - nice to have for betapriority:mediumsecurity

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions