- The subscription for rekeys is precomputed from the *next* epoch's address, per private channel and once for the base.
- The subscription for rekeys is precomputed from the *next* epoch's address, per private channel
- A receiver accepts a key only after: locating its blob, decrypting with the rotator↔recipient conversation key, checking the bound `scope` and `epoch` inside the plaintext, and matching`prevcommit` against the key it currently holds.
and once for the base. That is a `Filter` and belongs to the sync engine (§10), not here;`rekey_group`
- Only after holding **all**`n` chunks of one `(rotator, newepoch, prevcommit)` set, with none containing its locator, may a client conclude it was removed.
is what makes it derivable, and keeps a channel scope from being addressed at the base derivation.
-Send cap 80 blobs per event, accept cap 120 (Vector's documented erratum: the CORD-01 double envelope pushes 120 blobs past a 64 KB relay limit). Record the reason in a comment so nobody "fixes" it back.
-A receiver accepts a key only after: locating its blob, decrypting with the rotator↔recipient
- Compacted control heads are re-wrapped with their original signature intact, which is exactly why the control plane uses the plaintext seal.
conversation key, checking the bound `scope` and `epoch`**inside** the plaintext, and matching
- Two concurrent refoundings converge on the lexicographically lowest new base key; the heal is down-only.
`prevcommit` against the key it currently holds. The locator is deliberately *not* gated on open:
- Authority: a channel rekey needs `MANAGE_CHANNELS`, a refounding needs `BAN`, and in both the rotator must strictly outrank every removed target. Holding a key is never authority.
it derives from public keys alone (NIP-46 bunker parity) and so proves nothing, and the pairwise
decrypt plus the bound check are the whole gate.
- Only after holding **all**`n` chunks of one `(rotator, newepoch, prevcommit)` set, with none
containing its locator, may a client conclude it was removed. `collect_rotations`**unions** the blobs
of two chunks claiming one index rather than keeping the first: a catch-up chunk can legitimately
re-claim a slot, and a recipient dropped from the union would read as removed, which deletes the
community locally. `severed` is an OR across chunks for the same reason in reverse.
- Send cap 80 blobs per event, accept cap 120. The 80 is an erratum this reproduces: the CORD-01 double
envelope costs two NIP-44 base64 expansions, so 120 blobs measure ~77 KB and a 64 KB relay refuses
them. The accept cap stays at the spec's 120 so a peer at the spec limit still parses.
-`parse_blob_plaintext` takes the `community_id`, which the earlier sketch did not: the 104/136-byte
base forms carry the next epoch's Control Plane keys, and the 136-byte secret must derive to the
`control_pk` beside it — a community- and epoch-bound check. A **width past 136** is a form this client
predates, so it degrades rather than refusing: the frozen 72-byte prefix yields the root (membership and
every chat plane survive), the appended pair is kept when it verifies, and the rest freezes. Widths
between the defined forms fit no extension and stay malformed.
- Authority: a channel rekey needs `MANAGE_CHANNELS`, a refounding needs `BAN`, and in both the rotator
must strictly outrank every removed target. `rekey_authorized` is that whole rule — one permission check
plus `can_act_on_member` per target — so it is testable without minting a rotation, and an empty removed
set (a hygienic rotation, or a flip to Private) needs only the permission. Holding a key is never authority.
- Two concurrent refoundings converge on the lexicographically lowest new base key, and the heal is
down-only. `fork_winner` folds both into one call: the lowest candidate, returned only when it strictly
lowers a key already held, so a flaky fetch cannot re-fork a settled epoch.
-`Refounding` owns the epoch and the freshly minted pair and derives all three coordinates from them
(`read`, `signer`, and the signer's pk), so a caller cannot pair a root with the wrong epoch.
`plan_refounding` deliberately takes neither the fold nor the removed set: the authority gate is
`rekey_authorized`, one job each, and the pair travels in the base blobs.
- Compacted control heads are re-wrapped with their original signature intact, which is exactly why the
control plane uses the plaintext seal. `compact` is `stream::rewrap_seal` over the held seals, whose
signature grew a separate `signer` group in M7 — a split epoch reads under the rolled root but wraps as
the new control signer, so one group could not express it.
- Nothing in coop **mints**`severed` yet: it is a receiver-side rule, and the Server-Severing flow that
would set it awaits the invite-link wiring of §10.
Dissolution (CORD-02 §9) also lives here: a chainless, owner-signed `vsk 10` tombstone at`dissolved_group_key(id)`, plaintext-sealed, and a verifier **must** refuse any tombstone whose `eid` is not the community's own id (including the all-zero placeholder — accepting it lets an owner's genuine tombstone for one community be re-wrapped at another of theirs and kill it permanently). On sight the community is sealed read-only: subscriptions halt, nothing new is honored, existing history stays readable, and a member's delete of their own message is still honored.
Dissolution (CORD-02 §9) also lives here: a chainless, owner-signed `vsk 10` tombstone at
`dissolved_group_key(id)`, plaintext-sealed, and a verifier **must** refuse any tombstone whose `eid` is
not the community's own id (including the all-zero placeholder — accepting it lets an owner's genuine
tombstone for one community be re-wrapped at another of theirs and kill it permanently). On sight the
community is sealed read-only: `CommunityState.dissolved` records it, subscriptions halt, nothing new is
honored, existing history stays readable, and a member's delete of their own message is still honored.
## 9. Storage (`store.rs`) — local layer implemented in M1, state document in M2, fold bridge in M3, banlist in M5
## 9. Storage (`store.rs`) — local layer implemented in M1, state document in M2, fold bridge in M3, banlist in M5
@@ -761,7 +868,7 @@ pub struct CommunityState {
}
}
```
```
Landed in M2 with exactly the fields genesis can populate: `save_state`/`load_state` and `CommunityState::from_genesis`. Three fields the plan sketched are deliberately absent until something can fill them — `epoch_keys` (needs rekeys, M7), `dissolved` (needs the tombstone, M7), and `observed`/`guestbook`(need the guestbook ingest of §10). `control_pks` keyed by `u64` rather than `Epoch` and `heads` as a `Vec` rather than a `BTreeMap<[u8; 32], _>`, because serde_json cannot use a byte-array map key. `banned` is a `BTreeSet` — serde has no such problem with a `Vec`-like sequence.
Landed in M2 with exactly the fields genesis can populate: `save_state`/`load_state` and `CommunityState::from_genesis`. `dissolved` landed in M7, set from a verified tombstone. Two fields the plan sketched are still absent: `epoch_keys`, because `ChannelKeyRef` carries no key for it to hold (§14.11), and `observed`/`guestbook`, which need the guestbook ingest of §10. `control_pks` keyed by `u64` rather than `Epoch` and `heads` as a `Vec` rather than a `BTreeMap<[u8; 32], _>`, because serde_json cannot use a byte-array map key. `banned` is a `BTreeSet` — serde has no such problem with a `Vec`-like sequence.
M3 added the two bridges between this document and the fold:
M3 added the two bridges between this document and the fold:
@@ -929,6 +1036,7 @@ Each of these has burned a real implementation, or is a documented cross-client
- **Enforced in M4:** the chat plane's encrypted-seal requirement, at both publish and open; `channel`**and**`epoch` strict-equal to the plane whose key opened the wrap; a retired or unregistered rumor kind rejected on both sides; a target bearing tag that appears twice rejected outright; and a delete from anybody other than the message's own author refused by the caller's gate.
- **Enforced in M4:** the chat plane's encrypted-seal requirement, at both publish and open; `channel`**and**`epoch` strict-equal to the plane whose key opened the wrap; a retired or unregistered rumor kind rejected on both sides; a target bearing tag that appears twice rejected outright; and a delete from anybody other than the message's own author refused by the caller's gate.
- **Enforced in M5:** a Kick counts only where the roster admits its actor under `KICK` with a strictly higher rank and a resolvable citation; a Snapshot counts only from the epoch's refounder, with no owner fallback; a guestbook entry more than an hour ahead is dropped, and an out-of-range `ms` or a non-verb `3306` entry is malformed, not interpreted; a duplicated `vac` is rejected outright; and a delete is honored from its target's author unconditionally, or from another actor only where `can_delete` admits them.
- **Enforced in M5:** a Kick counts only where the roster admits its actor under `KICK` with a strictly higher rank and a resolvable citation; a Snapshot counts only from the epoch's refounder, with no owner fallback; a guestbook entry more than an hour ahead is dropped, and an out-of-range `ms` or a non-verb `3306` entry is malformed, not interpreted; a duplicated `vac` is rejected outright; and a delete is honored from its target's author unconditionally, or from another actor only where `can_delete` admits them.
- **Enforced in M6:** an invite fragment whose version is not this one is refused in either direction, as is one with a bad count or trailing bytes, and its encoding caps bootstrap relays at three; a bundle past the channel cap, past the epoch ceiling, or carrying a secret that is not 32 bytes of hex is refused before it is used, and one whose `owner` + `owner_salt` does not reproduce its `community_id` is refused outright; a bundle event off its coordinate, off its author, or unsigned is refused, and a tombstone at the coordinate reads as revoked; a Direct Invite's wrap, its seal signature and its rumor/seal author bind are all verified before the bundle is even parsed; and a Community List refuses to build past its membership cap or the NIP-44 plaintext cap.
- **Enforced in M6:** an invite fragment whose version is not this one is refused in either direction, as is one with a bad count or trailing bytes, and its encoding caps bootstrap relays at three; a bundle past the channel cap, past the epoch ceiling, or carrying a secret that is not 32 bytes of hex is refused before it is used, and one whose `owner` + `owner_salt` does not reproduce its `community_id` is refused outright; a bundle event off its coordinate, off its author, or unsigned is refused, and a tombstone at the coordinate reads as revoked; a Direct Invite's wrap, its seal signature and its rumor/seal author bind are all verified before the bundle is even parsed; and a Community List refuses to build past its membership cap or the NIP-44 plaintext cap.
- **Enforced in M7:** a blob's bound scope and epoch are checked inside the ciphertext, so a channel blob cannot be opened under the base scope or under another epoch, and a 136-byte base blob whose secret does not derive to the pk beside it is refused whole rather than adopting a split control plane; the locator is never gated, because it derives from public keys and proves nothing; a removal is never concluded from a partial chunk set, and two chunks claiming one index union their blobs rather than letting the loser's recipients read as removed; a rotation needs its permission and must strictly outrank every target, so a rotator holding the prior root or a demoted staffer holding the `control_root` is dropped; a plaintext-sealed rekey is refused; and a tombstone is refused unless its signed `eid` is this community's own id — the all-zero placeholder and a sibling community of the same owner included.
- Still owed to M8's audit: the byte caps, the 256-channel and 25-pin caps, and the write-side counterparts of the caps the folds already apply.
- Still owed to M8's audit: the byte caps, the 256-channel and 25-pin caps, and the write-side counterparts of the caps the folds already apply.
## 13. Milestones
## 13. Milestones
@@ -942,7 +1050,7 @@ Each of these has burned a real implementation, or is a documented cross-client
| M4 | Chat plane | ✅ `cargo test -p concord` (19 tests): a second holder folds a message's reactions, its author's edit and its author's delete, and ignores an edit or a delete from anybody else; a comment's root and parent survive the wire; a foreign channel, a replayed epoch, a plaintext seal, a retired kind and a duplicated target are each rejected; and history pages backwards across a rekey in order |
| M4 | Chat plane | ✅ `cargo test -p concord` (19 tests): a second holder folds a message's reactions, its author's edit and its author's delete, and ignores an edit or a delete from anybody else; a comment's root and parent survive the wire; a foreign channel, a replayed epoch, a plaintext seal, a retired kind and a duplicated target are each rejected; and history pages backwards across a rekey in order |
| M5 | Guestbook + member list + moderation | ✅ `cargo test -p concord` (23 tests): a second holder folds joins, leaves, a cited kick and a chunked snapshot into one memberlist in either arrival order, with a ban and a Grant deciding the edges; an uncited, unranked or owner-directed kick and a foreign snapshot are dropped; a future-dated entry, a malformed `ms`, a non-verb `3306`, a duplicated `vac` and a bad snapshot chunk are each refused; and a moderator delete lands only under a citation the roster admits, while a self-delete never asks |
| M5 | Guestbook + member list + moderation | ✅ `cargo test -p concord` (23 tests): a second holder folds joins, leaves, a cited kick and a chunked snapshot into one memberlist in either arrival order, with a ban and a Grant deciding the edges; an uncited, unranked or owner-directed kick and a foreign snapshot are dropped; a future-dated entry, a malformed `ms`, a non-verb `3306`, a duplicated `vac` and a bad snapshot chunk are each refused; and a moderator delete lands only under a citation the roster admits, while a self-delete never asks |
| M6 | Invites + Community List | ✅ `cargo test -p concord` (32 tests): the fragment's byte layout is pinned by golden base64url for the stock set, a dictionary mix and a verbatim literal, with a wrong version in either direction, trailing bytes and an over-cap count each fatal; a link round-trips as a full URL and as a bare naddr, and refuses a non-invite; a bundle round-trips while a revocation tombstone reads as revoked, and a wrong token, a squatter's author, a foreign `d`, a forged owner, a malformed secret and an over-cap channel count are each refused; a Direct Invite round-trips to its verified inviter and refuses a stranger's keys and a non-invite rumor; and the Community List keeps the earlier seed and the later current in either merge order, refuses to resurrect a tombstoned id until a newer join outruns it, and rebuilds on a second device with unknown fields intact while refusing an over-cap or oversized list |
| M6 | Invites + Community List | ✅ `cargo test -p concord` (32 tests): the fragment's byte layout is pinned by golden base64url for the stock set, a dictionary mix and a verbatim literal, with a wrong version in either direction, trailing bytes and an over-cap count each fatal; a link round-trips as a full URL and as a bare naddr, and refuses a non-invite; a bundle round-trips while a revocation tombstone reads as revoked, and a wrong token, a squatter's author, a foreign `d`, a forged owner, a malformed secret and an over-cap channel count are each refused; a Direct Invite round-trips to its verified inviter and refuses a stranger's keys and a non-invite rumor; and the Community List keeps the earlier seed and the later current in either merge order, refuses to resurrect a tombstoned id until a newer join outruns it, and rebuilds on a second device with unknown fields intact while refusing an over-cap or oversized list |
| M7 | Rekeys + refounding + dissolution | a removed member stops reading after a rekey; continuity and race rules tested; a tombstone seals the community and a foreign-id tombstone is refused |
| M7 | Rekeys + refounding + dissolution | ✅ `cargo test -p concord` (40 tests): a blob's bound scope and epoch are checked inside the ciphertext, so a channel blob cannot be opened under the base scope or under another epoch, and a staff secret that does not derive to the pk beside it refuses the whole blob; a removal is concluded only from a complete chunk set, and two chunks claiming one index union rather than drop a recipient's blob; continuity extends, gaps and forks, and the fork winner is the lowest key adopted only when it strictly lowers one already held; a rotation needs its permission and must strictly outrank every target, so holding a key is never authority; a full 80-blob chunk fits a 64 KB relay event and one more splits; compaction carries a settled head across a refounding with the original author's signature intact; and an owner's tombstone seals the community while an impostor's, the spec's all-zero `eid` and one re-wrapped from another community of the same owner are each refused |
| M8 | Pins + disappearing messages + hardening | pins verify from a keyless reader's view; expiry is refused at ingest and purged by the sweep; the audit of §12 is complete with a test per bullet |
| M8 | Pins + disappearing messages + hardening | pins verify from a keyless reader's view; expiry is refused at ingest and purged by the sweep; the audit of §12 is complete with a test per bullet |
Ordering is deliberately dependency-first: each milestone is usable on its own, and nothing in M2+ depends on a later milestone. The sync engine and its GPUI wiring (§10, §11) have no row of their own because they are cross-cutting: every plane they consume has to exist first, so they follow M8, and they are the milestone that applies §11's `chat::handle_notifications` routing fix.
Ordering is deliberately dependency-first: each milestone is usable on its own, and nothing in M2+ depends on a later milestone. The sync engine and its GPUI wiring (§10, §11) have no row of their own because they are cross-cutting: every plane they consume has to exist first, so they follow M8, and they are the milestone that applies §11's `chat::handle_notifications` routing fix.
@@ -965,7 +1073,11 @@ What M5 still defers, and to what: the **guestbook's fetch and ingest path** —
M6 closed at 32 tests, again with no dependency change and `Cargo.lock` untouched — the fragment codec is `data_encoding::BASE64URL_NOPAD` and the Direct Invite is nostr's own `nip59` builder and unwrapper, so nothing new was needed. New: `src/invite.rs` (bundle, link, fragment, Direct Invite) and `src/list.rs` (the Community List). In `stream.rs`, the seal and open primitives were factored out of `seal_content`/`decode_content` as `seal_bytes`/`open_bytes`, so the bundle's raw-token key and the List's to-self envelope share the crate's one NIP-44 shape instead of re-implementing it twice; `edition::TAG_SUBKIND` became public so the invite sub-kind tags do not restate the string.
M6 closed at 32 tests, again with no dependency change and `Cargo.lock` untouched — the fragment codec is `data_encoding::BASE64URL_NOPAD` and the Direct Invite is nostr's own `nip59` builder and unwrapper, so nothing new was needed. New: `src/invite.rs` (bundle, link, fragment, Direct Invite) and `src/list.rs` (the Community List). In `stream.rs`, the seal and open primitives were factored out of `seal_content`/`decode_content` as `seal_bytes`/`open_bytes`, so the bundle's raw-token key and the List's to-self envelope share the crate's one NIP-44 shape instead of re-implementing it twice; `edition::TAG_SUBKIND` became public so the invite sub-kind tags do not restate the string.
What M6 still defers, and to what: the **Invite List (13303) and the Registry (`vsk 8`)**, both to M7 and for the same reason — nothing in M6 consumes them, and a Registry write whose fold does not exist is dead wire, while M7's refounding is what reads the Registry's aggregate as the Public/Private source of truth (see §8.5); the **join gate that refuses a 51st membership** (the registry's, since `fits` protects the write rather than the add); and the byte-level cap audit, which is M8's.
What M6 still defers, and to what: the **Invite List (13303) and the Registry (`vsk 8`)** — both landed in M7, as planned; the **join gate that refuses a 51st membership** (the registry's, since `fits` protects the write rather than the add); and the byte-level cap audit, which is M8's.
M7 closed at 40 tests, again with no dependency change and `Cargo.lock` untouched. New: `src/rekey.rs` (the blob atom, the 3303 chunk set and its collection, continuity and the fork winner, the authority gate, refounding planning and compaction, and dissolution). The Invite List landed in `src/invite.rs` beside the bundle it bookkeeps, and the Registry became a Control Plane entity: `ControlFold.registries` keyed by creator, folded under `CREATE_INVITE`, its aggregate exposed as `ControlFold::is_public`, with `ControlWriter::set_registry` as the write side. `invite_links_locator` and M6's revocation machinery needed no change. In `stream.rs`, `rewrap_seal` gained a separate `signer` group, because a split epoch reads under the rolled root but wraps as the new control signer. In `store.rs`, `CommunityState.dissolved` records the seal; `list.rs`'s `canonical`/`union` became `pub(crate)` so the Invite List's merge shares them rather than restating the same total order.
What M7 still defers, and to what: **epoch key retention** — the blob atom delivers every plane key a refounding mints, but nothing can persist one, because `ChannelKeyRef` is `{id, name, private, epoch}` with no key field (§14.11); the **rekey subscription**, precomputed from the next epoch's address for every held private channel plus the base, which is a `Filter` and belongs with the sync engine (§10); the **Server-Severing flow** that would set `severed`, which awaits the invite-link wiring; and the **`Refound` seed** to `complete_memberlist`, which M7 can now mint but whose consumer is still the guestbook ingest of §10.
**M2's "created and published" is verified offline**: "published" is the two wraps existing and being openable by the invite keys, not a relay round-trip. There is no registry to publish through until §10, and a relay test would be testing the SDK, not the protocol.
**M2's "created and published" is verified offline**: "published" is the two wraps existing and being openable by the invite keys, not a relay round-trip. There is no registry to publish through until §10, and a relay test would be testing the SDK, not the protocol.
@@ -981,6 +1093,7 @@ What M6 still defers, and to what: the **Invite List (13303) and the Registry (`
8.**The Pin List's message-key disclosure has no public API (M8).** CORD-04 §7 Pins let a keyless reader verify a disclosure, which means revealing one message's NIP-44 keys rather than the plane's conversation key. `nostr`'s `nip44::v2::get_message_keys(conversation_key, nonce)` is a private `fn`, and both public entry points (`encrypt_to_bytes_with_nonce`, `decrypt_to_bytes`) take the whole conversation key — so the expansion has to be reproduced as `hkdf::expand_into(conversation_key, nonce, 76 bytes)` plus ChaCha20 and an HMAC-SHA256, exactly as Vector does, and round-tripped against nostr's own `encrypt` in a test. Read CORD-04 §7 in full at M8 before writing it: the reproduction is only worth it once the exact verification the pin must support is settled, and the alternative is contributing a `pub` message-key accessor upstream (we already track git master, so a patch branch or an upstream PR is viable and strictly better than a reproduction we must keep in sync).
8.**The Pin List's message-key disclosure has no public API (M8).** CORD-04 §7 Pins let a keyless reader verify a disclosure, which means revealing one message's NIP-44 keys rather than the plane's conversation key. `nostr`'s `nip44::v2::get_message_keys(conversation_key, nonce)` is a private `fn`, and both public entry points (`encrypt_to_bytes_with_nonce`, `decrypt_to_bytes`) take the whole conversation key — so the expansion has to be reproduced as `hkdf::expand_into(conversation_key, nonce, 76 bytes)` plus ChaCha20 and an HMAC-SHA256, exactly as Vector does, and round-tripped against nostr's own `encrypt` in a test. Read CORD-04 §7 in full at M8 before writing it: the reproduction is only worth it once the exact verification the pin must support is settled, and the alternative is contributing a `pub` message-key accessor upstream (we already track git master, so a patch branch or an upstream PR is viable and strictly better than a reproduction we must keep in sync).
9.**A remote signer is not plumbed.**`ControlWriter::publish`, `stream`'s seal builders and the invite builders all take `&Keys`. NIP-46 is a stated Vector feature (§10's task slots are already cleared on signer change), but making the writers async over a `NostrSigner` is a change to every builder, so it should be one deliberate pass rather than a patch per milestone. Nothing in M4–M8 depends on it except the UX of using a remote signer at all.
9.**A remote signer is not plumbed.**`ControlWriter::publish`, `stream`'s seal builders and the invite builders all take `&Keys`. NIP-46 is a stated Vector feature (§10's task slots are already cleared on signer change), but making the writers async over a `NostrSigner` is a change to every builder, so it should be one deliberate pass rather than a patch per milestone. Nothing in M4–M8 depends on it except the UX of using a remote signer at all.
10.**The fold is not incremental.**`fold_control` re-parses and re-folds the whole control edition window on every call, and each fold is up to `2 × entities + 8` passes. That is fine at the caps the spec sets (100 roles, 400-odd grants) and it is the simplest thing that is correct, but if the sync engine ends up calling it per event rather than per batch, the candidate maps and their parse belong in a cache keyed by edition id. Measure before optimizing.
10.**The fold is not incremental.**`fold_control` re-parses and re-folds the whole control edition window on every call, and each fold is up to `2 × entities + 8` passes. That is fine at the caps the spec sets (100 roles, 400-odd grants) and it is the simplest thing that is correct, but if the sync engine ends up calling it per event rather than per batch, the candidate maps and their parse belong in a cache keyed by edition id. Measure before optimizing.
11.**No plane key can be persisted (found in M7).** The rekey blob atom hands a receiver every key a rotation mints — the next `community_root`, the `control_root`, and a private channel's fresh key — and `CommunityState` has nowhere to put any of them: `ChannelKeyRef` is `{id, name, private, epoch}`, and the state's only root fields are the *current*`community_root`/`root_epoch`. So a client can verify a rotation and still lose it on restart, and it cannot read history written under a prior root or a prior channel epoch. M7 therefore left `epoch_keys` out rather than add a field with no key to hold. The fix is a schema change — `ChannelKeyRef` gains the key and its retired `priors`, and the state gains a root-per-epoch map — and it belongs with the sync engine that reads them back, since it changes `apply_fold` and the `13302` join material together. Armada already carries `priors` for exactly this reason.
assert_eq!(reopened.rumor_id,opened.rumor_id,"the rumor id survives");
assert_eq!(reopened.rumor_id,opened.rumor_id,"the rumor id survives");
assert_eq!(reopened.author,author.public_key());
assert_eq!(reopened.author,author.public_key());
@@ -512,6 +513,7 @@ mod tests {
rewrap_seal(
rewrap_seal(
&sealed(&edition,SealForm::Encrypted,&author),
&sealed(&edition,SealForm::Encrypted,&author),
&group(1),
&group(1),
&group(1),
Timestamp::from_secs(2)
Timestamp::from_secs(2)
),
),
Err(StreamError::NotRewrappable)
Err(StreamError::NotRewrappable)
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.