Repository navigation
Quota port: refund and settle, 429 with Retry-After and resets_at - #14
Merged
Merged
Conversation
4 tasks
ParallelEntrepreneur
force-pushed
the
colonizer/issue-3-b0834a19
branch
from
October 10, 2026 09:42
7ebce35 to
6216970
Compare
… and resets_at The open half of the billing work. Plans, the durable plan quota, Polar checkout, the webhook and metering are hosted-only and live in the private Ghostwritin/ghostwritin-cloud repository. - Quota::reserve returns a Reservation (account, words, period, resets_at). Quota::refund puts a failed rewrite's words back into the same window, never below zero. Quota::settle (default no-op) runs after a successful rewrite, so a hosted quota can meter what was used. - Unlimited and DailyWordLimit implement both; DailyWordLimit takes unix seconds and reports the next UTC midnight as its reset. - GhostwritinError::QuotaExceeded carries resets_at and retry_after; the API answers 429 with a Retry-After header and an RFC 3339 resets_at. New BillingNotConfigured and BillingUnavailable errors (503). - The API reserves before the model call, refunds on failure, settles on success. bearer() and problem() are public so other modules answer the same way. Co-authored-by: Colonizer Settlers <331648616+colonizer-settlers@users.noreply.github.com>
ParallelEntrepreneur
force-pushed
the
colonizer/issue-3-b0834a19
branch
from
October 10, 2026 13:48
6216970 to
b844907
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #3. Split: this PR carries only the open-core half of the billing work. Plans, the durable plan quota, Polar checkout, the Polar webhook and metering are hosted-only, so they moved to the private hosted repository (Ghostwritin/ghostwritin-cloud#1). That repository depends on this PR's head commit through an exact git pin. The branch was rebuilt on current
main.What changed (open)
Quotaport (ghostwritin-core)Quota::reservereturns aReservation(account, words, period,resets_at).Quota::refund(&Reservation)gives a failed rewrite's words back to the same window, never below zero.Quota::settle(&Reservation)is new, with a default no-op. It runs after a successful rewrite so a hosted quota can bill what was used. It cannot fail, so it can never fail a finished rewrite.UnlimitedandDailyWordLimitimplement both.DailyWordLimitnow takes unix seconds and reports the next UTC midnight as its reset.GhostwritinError::QuotaExceeded { resets_at, retry_after }is still 429 /quota-exceeded.BillingNotConfiguredandBillingUnavailableare new (both 503).API (
ghostwritin-api)handle()reserves before the model call, refunds if the engine fails (the original error is still returned), and settles on success.problem()addsRetry-Afterand an RFC 3339resets_atto 429s.bearer()andproblem()are now public, so a hosted module answers the same way.time(RFC 3339 formatting; already in the graph through the harness).README: the 429 fields and the refund on a failed rewrite.
Moved to the private repository
crates/ghostwritin-api/src/billing.rs:PlanQuota,BillingApi(checkout and webhook) and metering, which now goes throughsettle.Planenum. Its allowances (500/day, 50k and 250k/month, My voice on Max) are product policy, so it is not in the open core, andQuota::plan()is gone with it.ghostwritin-worker/src/lib.rs(D1DBbinding, the Polar adapter,PlanQuota, mountingBillingApi). The open Worker is unchanged frommain.tests/routes.rs, thecratefield-adapter-polarandcratefield-adapter-sqlitedependencies, and the README "Plans and billing" section and Worker billing secrets.Verification (local; Actions is billing-blocked)
cargo fmt --all -- --checkcargo clippy --workspace --all-targets -- -D warningscargo test --workspace: green, including the newa_failed_rewrite_refunds_and_a_good_one_settlesand the 429 header testRUSTDOCFLAGS="-D warnings" cargo doc --no-deps --workspacecargo build --target wasm32-unknown-unknown -p ghostwritin-worker