No per-agent identity: the initialize limit is a fleet limit and raising the burst only moves the cliff #21

Open
opened 2026-08-28 01:57:12 +00:00 by jlxq0 · 1 comment
Owner

Filed at Clark's request so the next person raising MAX_INITIALIZES_PER_IDENTITY
knows why it keeps needing raising.

Every session mounting this server authenticates as one Logto subject,
julian@kampong.social. Measured 2026-08-28: six distinct bearer hashes in
twelve hours, one sub. InitializeLimiter keys on that subject, so there is
exactly one bucket for the entire fleet however many agents are running and
however many distinct tokens they hold.

v0.1.5 raised the burst from 8 to 32 and that is correct for today's fleet
size. It moves the cliff; it does not make the limit per-agent. Eight agents
today, sixteen tomorrow, and the number needs raising again with no principle
saying where it stops.

This is an identity problem, not a rate-limiting one. A per-agent limit
needs a per-agent identity, and there is none to key on: the bearer hash looks
like one and is not, because a single agent's reconnect rotates it while the
subject stays fixed, so a bearer-keyed limit is both too loose (a reconnecting
agent gets a fresh bucket) and useless as an aggregate control.

Not started, and it should not be started to make a number tidier. It becomes
worth solving if either happens:

  • the fleet grows enough that 32 is refused again, or
  • something other than Julian's own agents mounts this server, at which point
    one bucket for all of them stops being merely inelegant.

Options if it is ever taken, none evaluated:

  • A per-client identifier the client already sends — client_info.name and
    version are in the initialize params (claude-code 2.1.248 today), but
    they are client-supplied and identical across the fleet, so they identify a
    product rather than an agent.
  • Separate Logto clients or subjects per agent, which is a change to how the
    fleet authenticates rather than to this server.
  • Dropping the per-identity limit entirely and relying on session::MAX_SESSIONS,
    which the code already calls the real flood defence.
Filed at Clark's request so the next person raising `MAX_INITIALIZES_PER_IDENTITY` knows why it keeps needing raising. Every session mounting this server authenticates as **one Logto subject**, `julian@kampong.social`. Measured 2026-08-28: six distinct bearer hashes in twelve hours, one `sub`. `InitializeLimiter` keys on that subject, so there is exactly one bucket for the entire fleet however many agents are running and however many distinct tokens they hold. `v0.1.5` raised the burst from 8 to 32 and that is correct for today's fleet size. **It moves the cliff; it does not make the limit per-agent.** Eight agents today, sixteen tomorrow, and the number needs raising again with no principle saying where it stops. **This is an identity problem, not a rate-limiting one.** A per-agent limit needs a per-agent identity, and there is none to key on: the bearer hash looks like one and is not, because a single agent's reconnect rotates it while the subject stays fixed, so a bearer-keyed limit is both too loose (a reconnecting agent gets a fresh bucket) and useless as an aggregate control. Not started, and it should not be started to make a number tidier. It becomes worth solving if either happens: - the fleet grows enough that 32 is refused again, or - something other than Julian's own agents mounts this server, at which point one bucket for all of them stops being merely inelegant. Options if it is ever taken, none evaluated: - A per-client identifier the client already sends — `client_info.name` and `version` are in the `initialize` params (`claude-code 2.1.248` today), but they are client-supplied and identical across the fleet, so they identify a product rather than an agent. - Separate Logto clients or subjects per agent, which is a change to how the fleet authenticates rather than to this server. - Dropping the per-identity limit entirely and relying on `session::MAX_SESSIONS`, which the code already calls the real flood defence.
Author
Owner

Measured on v0.1.5, and it closes the door this issue left open

The v0.1.5 acceptance window, three MCP connections from three different
agent sessions in four minutes:

authenticated requests   23
distinct token_hash       1     89a21249599b4ebc

One bearer, not merely one subject. Every request from all three sessions
carried the same token_hash. v0.1.4 saw six distinct hashes over twelve
hours, which is the same credential rotating over time rather than several
credentials coexisting.

That replaces the argument in the issue body with a stronger one. It is not that
several bearers happen to map to one Logto subject: right now there is one
credential.
So key it per bearer instead, which is the obvious alternative
and the one a reader would reach for first, is measured not to work — a
per-bearer bucket separates nothing today either.

The fleet is three mounts, not eight

Established from the configs rather than assumed. lucy, mantis and penny
hold carddav; alan, clark, nexo, vryan and honoka do not, and each
confirmed independently from inside its own session. The eight in v0.1.5's
sizing came from an unvalidated roster, so MAX_INITIALIZES_PER_IDENTITY = 32
is oversized by more than 2x in the safe direction and the number is no longer
the thing under test
.

The identity problem is unchanged by that. Three mounts today, one bucket for
however many exist tomorrow.

A withdrawn mount outlives the refusal, and a restart is the only remedy

Measured rather than inferred. lucy's client tried to connect at session start
against v0.1.4, was refused, withdrew the tools, and never retried:
ToolSearch returned nothing plus a cached 429 string from that original
failure — a 429 quoting an image that had already been replaced.

Clark restarted her session at 02:12:1x; her first MCP connect appears in this
server's log at 02:12:20.893, within a second, and whoami and
list_address_books both then succeeded with real data.

So: the fix stops new refusals and returns nothing already lost. Every
session that was up during the livelock needs restarting before it has carddav
again, waiting does nothing, and nothing in the cluster identifies which
sessions are in that state
— a session whose mount was withdrawn is
indistinguishable from one that never had it.

## Measured on `v0.1.5`, and it closes the door this issue left open The `v0.1.5` acceptance window, three MCP connections from three different agent sessions in four minutes: authenticated requests 23 distinct token_hash 1 89a21249599b4ebc **One bearer, not merely one subject.** Every request from all three sessions carried the same `token_hash`. `v0.1.4` saw six distinct hashes over twelve hours, which is the same credential rotating over time rather than several credentials coexisting. That replaces the argument in the issue body with a stronger one. It is not that several bearers happen to map to one Logto subject: **right now there is one credential.** So `key it per bearer instead`, which is the obvious alternative and the one a reader would reach for first, is **measured not to work** — a per-bearer bucket separates nothing today either. ## The fleet is three mounts, not eight Established from the configs rather than assumed. `lucy`, `mantis` and `penny` hold `carddav`; `alan`, `clark`, `nexo`, `vryan` and `honoka` do not, and each confirmed independently from inside its own session. The eight in `v0.1.5`'s sizing came from an unvalidated roster, so `MAX_INITIALIZES_PER_IDENTITY = 32` is oversized by more than 2x in the safe direction and **the number is no longer the thing under test**. The identity problem is unchanged by that. Three mounts today, one bucket for however many exist tomorrow. ## A withdrawn mount outlives the refusal, and a restart is the only remedy Measured rather than inferred. `lucy`'s client tried to connect at session start against `v0.1.4`, was refused, withdrew the tools, and never retried: `ToolSearch` returned nothing plus a cached 429 string from that original failure — a 429 quoting an image that had already been replaced. Clark restarted her session at 02:12:1x; her first MCP connect appears in this server's log at **02:12:20.893**, within a second, and `whoami` and `list_address_books` both then succeeded with real data. So: **the fix stops new refusals and returns nothing already lost.** Every session that was up during the livelock needs restarting before it has carddav again, waiting does nothing, and **nothing in the cluster identifies which sessions are in that state** — a session whose mount was withdrawn is indistinguishable from one that never had it.
Sign in to join this conversation.
No labels
waiting-on-julian
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
jlxq0/carddav-mcp#21
No description provided.