You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Added runtime partition detection to claim routing. The ownership gate now consults an optional
feed of the hubs observed asserting authority over a namespace, so a partition — a peer seen
holding a claim in a namespace this hub also believes it owns — refuses every grant until
ownership is re-established, even on the hub's own local grant path. multihub_fold's asserting_owners derives that feed from a follower's observed claims (the hub id that holds a
claim is observed owning the claim's namespace), and a hub wired with it through the opt-in observed_asserting_hubs source refuses a contested claim as partitioned. With no feed
configured, ownership resolves from the static map alone, exactly as before.
Closed the cross-hub claim-routing loop: a non-owning hub now forwards a claim for a namespace
it does not own to the hub that does and relays the verdict to the claimant. A hub configured
with claim_peers — a route to each owning hub — forwards a remote-owned claim automatically;
the claimant sees the owner's authentic claim_granted (with the real lease) or its denial,
just as for a local claim. The route is opt-in and fails closed: a hub with no route for the
owner, or one whose owner is unreachable, ungoverned, or contested, refuses the claim and names
the owner, exactly as before, so an unreachable owner never lets a claim be believed granted.
Two hubs that each own their own namespaces can now coordinate claims across a connection
without a shared filesystem or a global leader.
Added the forwarding half of cross-hub claim routing: a network client that asks a namespace's
owning hub to grant a claim and returns its authoritative verdict. It opens an on-demand
connection to the owning hub, sends the forwarded claim, and decodes the result the owning
hub's handler replies with — holding no standing outbound connection between claims. Every
transport failure (a refused or dropped connection, an error frame, a malformed or absent
result, or a timeout) fails closed as a single error, so a caller relays a real verdict or,
on failure, falls back to refusing the claim and naming the owner — an unreachable owner or a
split never lets a claim be believed granted. Wiring this into the non-owning hub's claim gate,
so a remote-owned claim is forwarded automatically, is the remaining slice.
Added the serving half of cross-hub claim forwarding: an owning hub now grants a claim
forwarded from another hub and relays the authoritative verdict back. When a non-owning hub
forwards a claim, the owning hub applies it through the same authoritative grant path a direct
claim uses — so the lease it produces is identical however the claim was routed — and answers
with whether it granted, the owning hub's id, and the grant fields the forwarding hub relays to
its client. Because a forwarded claim mutates lease state on a remote agent's behalf, the gate
fails closed at every step: the peer must be authorised by the hub's serving policy (a hub with
no policy accepts no forwarded claim at all), this hub must authoritatively and uncontestedly
own the namespace, and a malformed request grants nothing. Reaching out to the owning hub from
the non-owning side is the remaining slice; until then a non-owner still refuses and names the
owner.
Added the wire codec for forwarding a claim to the hub that owns its namespace. It names the
two shapes that exchange uses — a request carrying the namespace, the claimant the grant is made
under, the task id, and the original claim body the owning hub re-applies, and a result carrying
whether the owner granted, the owning hub's id, a human-readable detail, and the authentic grant
fields the forwarding hub relays back to its client. The codec is pure, with no network, clock,
or hub dependency, and decoding is defensive: a malformed request or result raises rather than
yielding a half-built shape, so a forwarding hub that catches it refuses the claim and relays no
grant it cannot trust. This is the first step toward granting a routed claim on the owning hub
rather than only telling the caller where to route it.
Added namespace-ownership resolution and its local enforcement on the claim grant path, the
first half of routing claims across hubs without merging them. A claim is mutual exclusion, not
a mergeable value, so claims are routed by namespace ownership: each namespace has exactly one
authoritative owning hub. NamespaceOwnership resolves a namespace to local, remote, ungoverned,
or partitioned (the last two fail closed); a hub configured with such a map refuses a claim whose
namespace — derived from the agent identity, as the ACL derives it — it does not own, naming the
owning hub in the claim_denied so the caller can route the claim there. The gate is opt-in: a
hub with no map grants every namespace, exactly as a single hub does today. Forwarding the refused
claim to the owning hub over a connection is not yet built; the caller is told the owner.
Added serving-side enforcement of the deny-by-default multi-hub pull gate, the counterpart of
the gating the following side already applies. A hub configured with a MultiHubServingPolicy
reads the certificate the peer presents on the live mutual-TLS connection and runs the same
federation-and-mutual-TLS composition before serving its event log: a peer with no operator
grant, a connection presenting no client certificate, or a certificate whose pin the policy
does not accept is answered with an empty snapshot — the same shape as "no new events", so the
refusal discloses neither the log nor whether the peer or its grant exists. The gate is
opt-in: a hub with no policy serves every peer as before, so no existing deployment changes.
The federation/mTLS pull gate is now enforced on both sides of a cross-host pull.