Keeping a record is not publishing it
Two separate gates, with two separate owners. Confusing them is the mistake this plugin is built to make impossible.
Storage, and publication
Keeping a record and publishing it are two different gates, and almost every argument about chat archives is really an argument about somebody treating them as one. Storage is the operator's switch. Publication is the member's, and only the member's.
The two were confused once in production, which is why the product now states them apart rather than trusting that they are obviously different. The retention sweep belongs to the first gate and is stated in full on the consent page rather than summarised again here. The retention sweep, in full
Derived, never stored
Publication is not a flag written onto a message. It is a view, derived from the consent table every time anything is read. That one decision is why withdrawal is immediate rather than a job that has to run.
By default, whatever you say here stays here, between us. I publish nothing of yours to the outside world unless you tell me to.
Consent publishes from now, never from before
Saying yes does not hand over a history. It publishes what is said after the yes, and nothing said before it.
The rule, exactly
Opting in publishes what a member says from that moment onward. It does not reach back and publish what they said last week, last month, or before they knew the bot was there. A message qualifies only if its timestamp is at or after the opt-in and falls inside no revoked interval.
Those revoked intervals are consent gaps, and they are permanent. Anything said while consent was withdrawn stays unpublished even if the member opts in again afterwards, so restoring consent can never quietly publish what was said while it was gone.
There is no command, console control or setting that reaches backwards.
Why it is built this way
The alternative, publishing a member's history the moment they agree, is the behaviour people expect from a chat archive and the reason they do not agree. It also makes consent unrevocable in practice: whatever went out cannot be recalled by changing a setting. Forward-only is what makes the other direction possible.
Two words, and one of them undoes the other
The whole mechanism is a member sending a message. No form, no account, and nobody able to send it for them.
Both routes, one write path
A member types /publish or /unpublish, which are exact and
act immediately, or asks in plain language and confirms. Both routes share one write
path, so there is no second implementation to drift, and the plain-language route is
not a lesser one.
Consent is always first-person. Asked to publish or unpublish on somebody else's behalf she refuses, and she refuses by name rather than failing quietly, because the member who did not ask for it is the one who needs to hear about it.
🔒 That spell is not mine to cast. Only {name} can open that door, and only for themselves…
Sorry - I could not process your command right now due to a temporary error. Please send it again in a moment.
The full text of both command replies is on the consent page, which owns them. This page quotes the refusal and the failure because they are the two a reader does not expect.
After withdrawal, a choice with no default
Taking words down and destroying them are different things, and the member decides which one happened.
🌙 Your words are hidden now, and no one can see them. What shall I do with them? Say *hide* and I keep them safely out of sight; you can ask me to bring them back whenever you like. Say *delete* and I destroy them instead, and then they are gone from my archive for good. Until you tell me, they stay hidden.
Hide: kept, and restorable
Hidden messages are retained and can be brought back, by that member and nobody
else. The phrase is
{wake}, restore my words, and until the member says one thing or the other the
messages stay hidden. There is no default for a destructive decision.
Words that mean hide
hide hidden keep keep them verbergen verstecken behalten
Delete: exact words only
Deleting destroys the messages one by one. The words that mean it are exact and there is no typo tolerance, which is deliberate: a near miss on an irreversible instruction should do nothing rather than nearly the right thing.
Words that mean delete
delete destroy löschen loeschen lösche loesche vernichten
What an evidence hold can and cannot do
An evidence hold defers a destruction and never the hiding: the messages come down from the public archive at once either way, and the deferred deletion runs by itself when the hold releases. A hold cannot be used to keep something visible.
🌙 … {held} of them did not destroy cleanly, so they are still here, hidden. I keep trying until they are gone, and the operator has been told.
At the instant, and one thing it cannot reach
Withdrawal is not a job that runs. It is a consequence of how publication is worked out in the first place.
Why there is nothing to wait for
Withdrawal takes effect at the instant, and the reason is the design decision in the first section rather than a fast job. Because publication is derived on every read, setting the revocation removes every message from all eleven public routes on the next render. There is no republish step, no cache to expire and no backfill to wait for.
The copy already taken
What the system cannot do is reach into somebody else's copy. A feed reader or a crawler that already fetched a page keeps what it fetched. The console says so rather than letting the operator believe otherwise, and so does this page.
| Question | Answer |
|---|---|
| The two gates | Storage: the per-bot "Archive capture" switch, the one-capturing-record-per-room rule, and the retention sweep bounding never-consented content. Publication: the consent table through derived views, never a stored flag. The two were confused once in production, which is why the product now states them apart |
| Forward-only, exactly | A message publishes only when its group timestamp is at or after the member opt-in timestamp AND falls inside no revoked interval, which are the consent gaps |
| Hide against delete | Hide is retained, restorable by that member alone. Delete is per message destruction; an evidence hold defers the destruction, never the hiding, and the deferred deletion runs by itself when the hold releases |
| How fast withdrawal bites | Instantly, by derivation: publication is a view evaluated on every read, so setting the revocation removes every message from all eleven public routes on the next render, with no republish step, no cache and no backfill. Honest caveat, on the console: a feed reader or crawler that already fetched a copy keeps it |
| Stores | Postgres for messages, links, consent with an append-only consent journal and gaps, holds and capture events; media on disk under MEDIA_ROOT, originals encrypted at rest with AES-256-GCM, the stripped public derivative plaintext; quarantine moved outside the served tree |
| Reaches outside | SimpleX relays, which are the transport itself; one optional one-time video thumbnail fetch only when SimpleX delivered none, served locally afterwards. No screening provider is configured, and the null provider transmits nothing. No model on the consent path |
| Failure | A failed consent command says so, with a dashboard alert: a consent decision is never silently dropped. A failed deletion retries durably and says so |
The count was right, the sentence was not
Found by the operator against a running system, in a question about his own project history.
The operator asked the archive which session decided a switch from one TLS library to another. She answered with a count, and the sentence around the count claimed the switch had happened. It had not: what she had was two messages containing the terms.
I found 2 moments where this group spoke of the switch from mbedTLS to OpenSSL
🔍 I count {n} public messages in this group matching *{query}*. A word match, mind, not a memory of what was meant.
What actually changed
The count was never wrong. What was wrong was the sentence it sat in, which turned a term match into a memory of what was meant. The line that ships now returns the same number and says what kind of thing it is, which costs nothing and is the difference between a search and a claim.