Skip to content

feat(call): per-team daily allowance on catalog endpoints served on treg's key - #545

Open
stonexer wants to merge 1 commit into
mainfrom
feat/free-endpoint-allowance
Open

stonexer wants to merge 1 commit into
mainfrom
feat/free-endpoint-allowance

Conversation

@stonexer

@stonexer stonexer commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

Why

A $0 catalog endpoint served on treg's key passes every money gate trivially: the hold is 0 and the daily cap adds 0. One looping client can therefore spend the shared vendor key's whole quota and every other team's access with it, after which its retries are refused by the capacity breaker for as long as the vendor holds the key, and each refusal counts as a platform failure on the health dashboard. That is exactly what happened on ContactOut's free availability routes: the vendor rate-limited the shared key, the breaker locked the two endpoints, and the client kept retrying for hours.

The trial pools already solve this for three whole-provider $0 rates. Most zeros in the catalog are single free routes on paid providers and had no brake at all.

The mechanism is deliberately generic so a paid route can carry the same figure later without a table rename or a new error code; today only free routes need one.

What

  • cost.calls_per_team_day on any cost block: calls per team per UTC day on treg's key. The mechanism is price-agnostic; only the default is not: a type: free endpoint that declares none falls back to TREG_FREE_ALLOWANCE_PER_TEAM_DAY (default 5,000; 0 = no default), while a paid endpoint has an allowance only when it declares one, because money already brakes it. The case for a paid figure is a vendor whose per-key daily quota one team could spend for everyone. In practice the field is expected to live on free routes. The validator refuses any figure below 1.
  • _enforce_endpoint_allowance in application.call.reserve, beside the trial allowance, takes the slot inside the reservation transaction with one conditional upsert of a new EndpointAllowance row (Alembic 0040): the WHERE is the check and the SET is the count, so concurrent calls cannot overshoot and a refused call writes nothing. Counted from its own row, never from callrecord, because audit rows are shed under load. Admitted attempts count, failures included, because the vendor's limits count attempts. Fail-closed like the trial pools.
  • Refusal: 429 endpoint_allowance_reached with provider, endpoint_id, allowance_per_day, used_today, resets_at and a connect-your-own-key message; audited refused_by=cap, so a client retrying against it is a product refusal on the dashboard, not a platform failure. A team's own key never meets the gate (non-negotiable 1); billed OAuth calls, owned async polls and authorized free final fetches skip it.
  • Catalog exposure: cost_view attaches the effective calls_per_team_day beside every price that carries one (never beside a trial pool's own field), so a bare zero never reads as unlimited in catalog_get or the web.
  • Figures: ContactOut's five free routes declare 1,000. Seven discovery and count routes whose legitimate use runs above the default (hunter domain-finder and discover, apollo people search, quickenrich people search, crustdata identify, tomba email count, leadsforge people search) declare higher figures so no current user is affected.

Tests

tests/test_endpoint_allowance.py: bites at the declared number, unbilled, before the vendor, refused_by=cap; failed attempts count; a refused call writes nothing; exact under 20 concurrent calls; per team; per UTC day; per endpoint not per provider; deployment default, zero default, own figure over default; own key never gated; a paid route is untouched by the default and gated by a declared figure; fail-closed; catalog exposure; validator rules. The call-architecture allowlist gains endpoint_allowance_slot; alembic head/model parity passes. Full suite green locally.

Fragments updated

architecture/money.md (§ The endpoint allowance), architecture/catalog.md (the cost field), interface/api.md (the refusal), architecture/data-model.md, architecture/contactout.md, ops/deploy.md; llms.txt, skill.md and the generated plugin mirrors; AGENTS.md write list. drift.sh also flags apollo.yaml, hunter.extended.yaml and leadsforge.yaml as sources with no fragment; that gap predates this PR (only an allowance line changed in each).

Deploy notes

Migration 0040 is additive. The default applies at once after python -m treg upgrade; no environment change is required. Watch refused_by=cap refusals with error=endpoint_allowance_reached after rollout: a legitimate team hitting the default is the signal to raise that endpoint's YAML figure.

@github-actions github-actions Bot added area:dashboard The web dashboard / landing / tutorials area:docs Documentation & design fragments labels Sep 17, 2026
…reg's key

Money brakes nothing at $0: the hold is 0 and the daily cap adds 0, so one
looping client can spend the shared vendor key's whole quota and every other
team's access with it, after which its retries land on the capacity breaker
for as long as the vendor holds the key. This is what happened on ContactOut's
free availability routes.

Any cost block may now declare `calls_per_team_day`, calls per team per UTC
day on treg's key. The mechanism is price-agnostic; only the default is not: a
`type: free` block that declares none falls back to
`TREG_FREE_ALLOWANCE_PER_TEAM_DAY`, while a paid block has an allowance only
when it declares one, because money already brakes it. The reservation takes
the slot with one conditional upsert of a new `EndpointAllowance` row
(revision 0040) in the same transaction, so the gate and the count are one
statement, concurrent calls cannot overshoot, and a refused call writes
nothing. Admitted attempts count, failures included, because the vendor's own
limits count attempts. Fail-closed like the trial pools. A team's own key
never meets the gate. The refusal is `429 endpoint_allowance_reached`,
audited `refused_by=cap`, so a client retrying against it is a product
refusal, not a platform failure.

ContactOut's five free routes declare 1,000. Seven discovery and count routes
whose legitimate use runs above the default declare higher figures. The
validator refuses a figure below 1; `cost_view` shows the effective figure
beside every price that carries one so a bare zero never reads as unlimited.

Fragments: money.md (the gate), catalog.md (the field), api.md (the refusal),
data-model.md, contactout.md, ops/deploy.md; llms.txt, skill.md and the plugin
mirrors; AGENTS.md write list; the call-architecture allowlist.
@stonexer
stonexer force-pushed the feat/free-endpoint-allowance branch from 791de35 to 83d5fdd Compare September 17, 2026 02:04
@stonexer stonexer changed the title feat(call): per-team daily allowance on $0 endpoints served on treg's key feat(call): per-team daily allowance on catalog endpoints served on treg's key Sep 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:dashboard The web dashboard / landing / tutorials area:docs Documentation & design fragments

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant