Skip to main content

Credits

Players pay for building in credits. One credit is one cent of a plan's face value, and everything a player uses is charged at twice what it costs the platform. The prices are in src/lib/credit-prices.ts (PLANS, MARKUP, MODEL_PRICES and the asset prices), the ledger in src/lib/credits.ts, the usage screen in src/lib/usage.ts.

Usage is always recorded and taken off the balance, so the usage screen is right whether or not credits are enforced. CRESCENT_METERING=on enforces them.

Metering offMetering on
The builder's Claude callsRead from the run's log when the run ends (recordBuilderUsage)Charged by the edge's meter, call by call
Nova, box timeCharged as they happenSame
Art, sounds and 3D modelsMade without asking, then recordedWait for the player's yes in the confirm box; paid up front, failures refunded
At zeroNothing is refusedBuilds stop and ship their last checkpoint; chat answers 402

What is charged​

WhatHow
The builder's Claude callsPer call, by model and tokens (input, output, cache reads and writes), plus web searches
Nova's model callsPer step, by agent/hooks/meter.ts on eve's step.completed event
The run's sandboxOn every 15-second poll (pollRun)
Art, sounds and 3D modelsUp front, after the player approves

Not charged to the player: a game's cover art, and merge runs for community builds. Both are recorded at cost with no credits taken.

Monthly credits​

Every balance goes back to the monthly allowance once a month, on the day of the month the account was made. Whatever was left does not carry over. The refill is lazy: refillIfDue runs whenever a balance is read or a call is authorized, and writes a monthly ledger row once per period.

VariableDefaultWhat
CRESCENT_MONTHLY_CREDITS1000What every balance refills to
CRESCENT_STARTER_CREDITSnoneA one-time bonus on top of the first month
CRESCENT_OWNER_USER_IDSnoneAccounts that also see the platform's cost and every player's usage

The ledger​

users.credit_balance holds the balance, and credit_ledger every change, in micro-credits (1 credit is 1,000,000). Each line also records what it cost the platform (cost_usd). Every change carries a unique key, so repeating one changes nothing: that is what makes the edge's and the workflow's retries safe. A balance never goes below zero.

npm run credits:grant -- <userId> <credits> [note] grants credits by hand.

The usage screen​

GET /api/app/usage returns what is left of the month's credits, when they refill, and a log grouped for people: one entry per build (its Claude tokens per model, sandbox minutes, assets), one per day of chat with Nova, and refills, grants and refunds on their own. ?back=1 is the month before.

The builder's meter​

With metering on, the sandbox never gets an Anthropic key. Claude Code gets ANTHROPIC_BASE_URL pointing at the edge (/llm/<runId>) and an llm token good for that run only (llmToken() in src/lib/realtime.ts). The edge Worker holds the real key (edge/src/index.ts, edge/src/meter.ts).

  1. Before each call the edge asks the server (POST /api/internal/meter/authorize). A run that has ended, a model not in MODEL_PRICES or a player at zero is refused. Otherwise the answer says how many output tokens the balance pays for, and that becomes the call's max_tokens.
  2. When Anthropic reports the input (the stream's first event), the edge charges it (POST /api/internal/meter/charge). If that used up the balance, the call is cut off.
  3. When the call ends, however it ends, the edge charges the output: Anthropic's final count, or an estimate from the text that went by if the call was cut off.

At zero the next call gets a 402 with billing_error, which makes Claude Code exit. The next poll finds the balance gone, stops the script and ships the last checkpoint. Nova gets [Build stopped].

Only /v1/messages and /v1/messages/count_tokens are reachable through the meter. The meter lives at the edge rather than in the box because the builder has full permissions in its box and follows player-written requests, so anything counted or kept there could be read or changed.

Nova​

POST /eve/v1/app/chats/:chatId/messages answers 402 with code: "out_of_credits" at zero, and make_game and change_game will not start a build. The meter hook charges each step and ends the turn when that used up the balance. Moonspell's own system messages to Nova, such as [Build ready], are charged but never cancelled, because the player still has to hear what happened.

The confirm box​

Art, sounds and 3D models wait for the player when metering is on. After the builder's pass the workflow prices the builder's list (quoteAssets), stores the quote on the run, publishes an assets event and waits on the hook run-assets:<runId> for up to ten minutes. The app shows what would be made, the price and the balance.

  • Yes: POST /api/app/runs/:id/assets with {approve: true} takes the whole price up front, or answers 402 when the balance is short. When the assets are done, settleAssets returns the share of whatever failed.
  • No, or no answer: the version ships with its placeholders, and nothing is charged.

Going live​

The edge needs the server's meter routes, and the server needs the edge's meter, so the order matters:

  1. The edge Worker needs a Workers plan with more CPU per request than the free plan, since it reads each stream.
  2. Apply the migrations: npm run db:migrate.
  3. Set the edge secrets ANTHROPIC_API_KEY and INTERNAL_API_SECRET (the server's value) and deploy the edge.
  4. Deploy the server with CRESCENT_METERING still off, so the routes exist.
  5. Grant credits to testers, then set CRESCENT_METERING=on and redeploy.

To turn it off, unset CRESCENT_METERING and redeploy. Runs go back to the key on the server, and balances stay as they are.