Skip to content

Quota pools; accepting_new and quota_pool on POST /fmsgid - #7

Merged
markmnl merged 1 commit into
mainfrom
quota-pools
Oct 10, 2026
Merged

markmnl merged 1 commit into
mainfrom
quota-pools

Conversation

@markmnl

@markmnl markmnl commented Oct 10, 2026

Copy link
Copy Markdown
Owner

Summary

Quota pools. Addresses can now share one quota, e.g. linked addresses such as @alice_bot@example.com sharing @alice@example.com's.

  • dd.sql: new nullable address.quota_pool text referencing address (address_lower) (on delete set null), indexed. Added idempotently (add column if not exists, a catalog-checked do block for the FK, create index if not exists); a nullable column without a default doesn't rewrite the table, so it is safe to re-run on a populated database.
  • GET /fmsgid/:address: when quota_pool is set, the limit* fields are the pool owner's, and the six usage fields are summed over the owner plus every address whose quota_pool is the owner. An unpooled address's usage includes its pool members' too, so the owner's own check counts its members' usage. acceptingNew stays per address. Response shape unchanged apart from an optional quotaPool (pool owner's address), omitted when not pooled. Defensive left join keeps an address's own limits if its owner were missing.
  • README documents the column for whatever syncs the identity provider: it needs UPDATE (quota_pool) on address.

POST /fmsgid accepts optional accepting_new (bool) and quota_pool (address; "" or the address itself clears it), applied to both new and existing addresses, and returns {"created":bool,"reinstated":bool} (reinstated = existed, wasn't accepting, now is). Requests without the new fields behave exactly as before (201/200, existing rows untouched). Pools are one level deep: quota_pool must name an existing address in no other pool, and an address that owns a pool can't join one, otherwise 422 {"error":...}. The insert names only the given columns so everything else still takes the column defaults.

This gives a client that creates linked addresses (e.g. fmsg-webapi sub-accounts) one call to create-or-reinstate an address in its owner's pool, and one to stop it accepting new messages when it's deleted.

Test plan

  • go test -race ./..., go vet ./..., gofmt clean
  • DB tests against a throwaway Postgres 17 (FMSGID_TEST_DATABASE_URL): pooled limits and summed usage, owner sees members' usage, unpooled unchanged, missing owner refused (FK, 422) and deleted owner reverts members to their own limits, quota_pool set/clear/one-level rules, accepting_new disable/reinstate and reinstated, dd.sql re-applied gives one FK and one index
  • Applied the previous dd.sql to a fresh DB, inserted rows, then applied the new one twice with ON_ERROR_STOP=1
  • EXPLAIN of the pooled usage query uses address_pkey + address_quota_pool (BitmapOr) and address_tx_addr_type_ts

🤖 Generated with Claude Code

Quota pools let addresses share one quota. The new nullable
address.quota_pool column (a foreign key to address_lower, indexed,
added idempotently by dd.sql) names the pool owner; NULL means the
address is its own pool. GET /fmsgid/:address reports a pooled
address's owner's limits and the usage summed over the owner and every
address in its pool, and an owner's usage includes its members' too.
accepting_new stays per address. The response gains an optional
quotaPool field naming the pool owner.

POST /fmsgid accepts optional accepting_new and quota_pool, applied to
a new address or an existing one, so a caller can stop an address
accepting new messages, reinstate it, or pool it. It now answers with
{"created":bool,"reinstated":bool}; reinstated is true when an address
that wasn't accepting new messages now is. Requests without the new
fields behave as before. Pools are one level deep: quota_pool must name
an existing address in no other pool, and a pool owner can't join one
(422 otherwise).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@markmnl
markmnl merged commit 9eef742 into main Oct 10, 2026
1 check passed
@markmnl
markmnl deleted the quota-pools branch October 10, 2026 06:33
markmnl added a commit to markmnl/fmsg-docker that referenced this pull request Oct 10, 2026
…23)

fmsgid main (quota pools, markmnl/fmsgid#7) selects address.quota_pool,
which the copied fmsgid DD here didn't have, so every address lookup
failed with 500 ("column a.quota_pool does not exist"). The integration
tests then stopped at creating API keys: create-delegation failed
checking the owner in fmsgid, and only the rotate fallback's error
("grant not found") was printed.

Refresh docker/postgres/init/002-fmsgid-dd.sql with scripts/update-dd.sh
from fmsgid main. When both create-delegation and rotate-delegation fail,
print both errors.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant