diff --git a/cli/README.md b/cli/README.md index 519e277554..fb4c9af8d5 100644 --- a/cli/README.md +++ b/cli/README.md @@ -678,7 +678,7 @@ also carried on-relay as the encrypted, fragmented kind:33302 Community List | `amy concord import` | Fetch + decrypt this account's Community List — the kind:33302 fragments plus the retired kind:13302 (carries heldRoots, CORD-06). | | `amy concord channels COMMUNITY` | List a community's channels; `readable` is false for a private channel whose key this account does not hold (CORD-03 §1). | | `amy concord send COMMUNITY CHANNEL TEXT` | Post a message (CHANNEL = `general`\|name\|id). A private channel posts on its own key's plane; without a held key it fails with `no_channel_key` instead of falling back to the community-wide plane. | -| `amy concord read COMMUNITY CHANNEL [--limit N] [--epoch N] [--root HEX]` | Read a channel's messages (default 50); `--epoch`/`--root` read a prior epoch's plane (public channels; a private channel reads its held key's plane). | +| `amy concord read COMMUNITY CHANNEL [--limit N] [--epoch N] [--root HEX]` | Read a channel's messages (default 50); `--epoch`/`--root` read a prior epoch's plane (public channels; a private channel reads its held key's plane). Banned members' messages are left out and counted in `hidden_banned`. | | `amy concord invite COMMUNITY [--base URL]` | Mint + publish a shareable invite link (at most 3 bootstrap relays ride in the fragment, CORD-05 §3; the bundle names this account as creator). | | `amy concord revoke COMMUNITY TOKEN\|URL` | Retire a link you minted: publishes a `vsk=9` tombstone at its coordinate, then records it in your Invite List. | | `amy concord join URL` | Redeem an invite link, save the community, and publish a Guestbook Join echoing the link's attribution (CORD-05 §1/§6). | diff --git a/cli/src/main/kotlin/com/vitorpamplona/amethyst/cli/commands/ConcordChannelCommands.kt b/cli/src/main/kotlin/com/vitorpamplona/amethyst/cli/commands/ConcordChannelCommands.kt index 312c3b88d3..073a326bb4 100644 --- a/cli/src/main/kotlin/com/vitorpamplona/amethyst/cli/commands/ConcordChannelCommands.kt +++ b/cli/src/main/kotlin/com/vitorpamplona/amethyst/cli/commands/ConcordChannelCommands.kt @@ -157,13 +157,17 @@ object ConcordChannelCommands { // The channel plane is NIP-42-gated to its own derived stream key; register it so the drain authenticates. ctx.registerConcordStreamKeys(relays, listOf(channel.secretKey)) val wraps = ctx.drain(relays.associateWith { listOf(ConcordActions.planeFilter(channel.publicKeyHex)) }, pendingOnAuthRequired = true).map { it.second } - val msgs = ConcordActions.channelMessages(wraps, channel, channelId, epoch).takeLast(limit) + // A banned member's messages are hidden, as every client shows the channel (CORD-04 §4); + // the count of what was hidden stays in the output so interop checks can see it arrived. + val (banned, visible) = ConcordActions.channelMessages(wraps, channel, channelId, epoch).partition { state.authority.isBanned(it.author) } + val msgs = visible.takeLast(limit) Output.emit( mapOf( "channel" to channelId, "epoch" to epoch, "plane" to channel.publicKeyHex, "count" to msgs.size, + "hidden_banned" to banned.size, "messages" to msgs.map { mapOf("event_id" to it.id, "author" to it.author, "content" to it.content, "created_at" to it.createdAt) }, ), )