Skip to main content

Module limits

Module limits 

Source
Expand description

Per-plan limits and the request envelope (paid-path plan S3, gaps G4 and G5).

Until now the server had no abuse envelope at all: no quota, no timeout, no concurrency bound — safe only because the one caller was the operator. This module is the table every limit is read from, keyed by the plan recorded on the key (see crate::keys), so a limit lives in exactly one place instead of being a literal scattered through the handlers that enforce it.

Two plans exist. self-hosted is the operator’s own keys: every limit is None, the envelope does not apply — an operator is trusted with their own box. hosted is the paid tier from the plan: US$19/month per key, 25 active indexes, 10,000 queries per month, a 50,000-file cap per index, and the ingest caps S2 shipped as interim constants (now this table’s hosted row). A plan name the table does not know — an admin can type anything after --plan — resolves to the hosted row, never the operator’s: an unrecognized plan must fail toward the tenant envelope, not toward unlimited.

“Per month” is the UTC calendar month: the metering log’s timestamps are UTC, a key’s usage in a month is the count of its records whose ts falls in that month, and the quota resets when the UTC month rolls over. There is no rolling window and no per-key anniversary — the count is the log, so the rule had to be one a reader of the log can apply with a text search.

Two bounds are not per-plan because they protect the host, not a tenant: GLOBAL_MAX_CONCURRENT_QUERIES caps concurrent query requests across all keys. A query loads its whole graph into RAM (~121 MB at 46k nodes by the project’s scale gate; a hosted index at the 50,000-file cap extrapolates to roughly 600 MB), and the Aetheris host has 4 vCPUs and ~10 GB of headroom shared with a local-model stack — 4 concurrent loads is one per vCPU, worst case ≈2.4 GB of graph RAM, and more concurrency would only queue CPU-bound work behind itself.

Structs§

Envelope
The process-wide envelope state: the global query semaphore and one index-operation semaphore per key.
PlanLimits
One plan’s limits, resolved. None is always “no limit” — the field is absent from the envelope, not set to a large number, so an unlimited plan pays no bookkeeping for limits it does not have.

Constants§

GLOBAL_MAX_CONCURRENT_QUERIES
Concurrent query requests admitted across all keys and plans — the graph-load semaphore of gap G5. See the module docs for the host arithmetic. Requests past the cap wait for a slot rather than being refused: a query is cheap to queue and expensive to retry, the opposite of an index operation.
HOSTED_MAX_ACTIVE_INDEXES
Active indexes a hosted key may hold at once (the plan’s offer).
HOSTED_MAX_CONCURRENT_INDEX_OPS
Index operations one hosted key may run at a time. One, on purpose: the permit is held from the active-index quota check through the ownership record, so a key’s creations are serialised and the quota cannot be raced past; and on a shared 4-vCPU box, one tenant’s extraction never crowds out another’s queries.
HOSTED_MAX_FILES_PER_INDEX
Files one hosted index may be built from (the plan’s repo cap).
HOSTED_MAX_QUERIES_PER_MONTH
Metered queries a hosted key may make per UTC calendar month.
HOSTED_QUERY_TIMEOUT_SECS
Wall-clock budget for one hosted query. The scale gate’s queries answer in well under a second at 46k nodes; 30 s is generous headroom over the largest graph the hosted file cap admits, while still bounding how long one request may hold a graph-load slot.

Functions§

plan_limits
The limits a plan name resolves to. Only self-hosted is unlimited; hosted — and any name the table does not know — gets the tenant envelope (see the module docs for why unknown fails toward hosted).
query_envelope
Middleware over the query routes: the global concurrency cap and the caller’s per-plan query timeout.