update
This commit is contained in:
+23
-5
@@ -274,7 +274,10 @@ than writing off a relay that only wanted to authenticate.
|
||||
reports what it saw: `oldest_ms`/`newest_ms` feed the caller's `ChannelCursor`,
|
||||
`exhausted` is earned only by a short page *after* history was seen, and an
|
||||
all-empty answer sets `failed` so a later round re-asks instead of sealing the
|
||||
channel at "no more history". Page down with `Window::older_than(seen.oldest_ms)`,
|
||||
channel at "no more history". `unreadable` counts the wraps the page reached that
|
||||
no held key could open — sealed past the cutoff their key's rotation set, or bound
|
||||
to another channel — because those are history the reader is missing, not history
|
||||
that is not there. Page down with `Window::older_than(seen.oldest_ms)`,
|
||||
open a channel with `Window::opening(cursor)` (wide cold, `newest_ms - 60s` warm),
|
||||
and read the region between two cursors with `Window::between(..)`. `query_rumors`
|
||||
is the read path when the group keys are gone; pass `kinds` to budget rows apart
|
||||
@@ -284,6 +287,18 @@ author and message times without re-opening a wrap it already cached, and
|
||||
`cache::purge_expired(client, &channel, now)` runs at the top of every round — the
|
||||
timer is cooperative, so the local store is the artifact that has to forget.
|
||||
|
||||
`sync::fold` counts the same thing over the whole store, per channel, in
|
||||
`Snapshot.unreadable`. `sync_round` sums a round's pages into
|
||||
`Progress.unreadable`, and `Community` keeps both per channel: `progress(channel)`
|
||||
is the last completed round, `unreadable(channel)` is the count, and
|
||||
`missing_key(channel)`, `channel_removed_at(channel)`, `removed_at()` and
|
||||
`stranded()` are the rest of the honest read surface a panel needs to tell an
|
||||
empty room from one it cannot read. `Community::due(channel)` is the other half —
|
||||
whether a round is worth asking for yet (a round for `Older` and an explicit retry
|
||||
never ask it), and `Community::tick`, driven by `CommunityRegistry` once per
|
||||
`community::MIN_ROUND_INTERVAL`, re-folds and re-rounds the active channel of a
|
||||
community whose last round is older than `STALE_AFTER`.
|
||||
|
||||
`ChatAction::TimerNotice { seconds }` is a policy notice, not a message: render it
|
||||
as an inline row only when its author passes
|
||||
`control.roles.is_authorized(&author, &owner, Permissions::MANAGE_METADATA)`.
|
||||
@@ -717,7 +732,10 @@ client.subscribe(filter).with_id(sub_id).await?;
|
||||
retired root's Control signer and the publish time that retires it), and each
|
||||
`ChannelKeyRef.priors` keeps every channel key a rotation stepped off. The read
|
||||
side derives planes from all of them, and a retired key's `retired_at` is a hard
|
||||
read cutoff at both `history::page` and `sync::fold`. The residual gaps: the
|
||||
panel does not yet render the `removed`/`stranded` state `Community::removed_at`
|
||||
and `Community::stranded` carry (phase 4), and a base removal is not yet enforced
|
||||
at send time — the composer still has the old root to write under.
|
||||
read cutoff at both `history::page` and `sync::fold`. The state
|
||||
`Community::removed_at`/`Community::stranded`/`channel_removed_at` carries is
|
||||
rendered as a notice **and** enforced at write time: `Community::send` refuses
|
||||
when `channel_secret` is `None` (a removal, a strand, a channel cut, or a key we
|
||||
never held), because a wrap sealed under a superseded root would reach nobody who
|
||||
rotated. The residual gap: this client does not adopt a rotation it published
|
||||
itself, because there is no rekey writer yet.
|
||||
|
||||
Reference in New Issue
Block a user