← all audits

Discord: DMs, groups, servers

What works where, and the false pass that could bite.

Discord scopes for OSL

Headline

The canonical code has the pieces of a one-recipient Discord DM path, but no Discord scope has a recorded live two-identity round trip, and group-DM and server-channel protection are not implemented. The dangerous part is that the overlay can find a Message #channel composer while still encrypting for one selected friend, so “the strip appeared” can be a convincing false pass. [CODE integration/full 4d13b8a, apps/osl-hub-ui/src/services.ts:540-553; CODE apps/osl-hub/src/broker.rs:463-509; CODE apps/osl-hub/src/native_discord_adapter.rs:319-394,26903-26918]

That contradicts the plan's checked scope and delivery tasks. Their own evidence says TASK 0915 was blocked and TASK 0940 sent nothing and produced zero receiver reads. The spec itself says loopback is not two-party proof, and the product status says no recorded two-party Discord proof exists. The product must keep saying “not proven” until a real run succeeds. [EVIDENCE evidence/0915.md:1-8,53-62; EVIDENCE evidence/0940.md:1-18,87-113; SPEC 01-FULL-SPEC.md:102-108; TASKS 0929 and 0940, todo/06-chat-apps.txt:375-385,459-483; CODE apps/osl-hub-ui/src/services.ts:547-553]

What a protected Discord message is

OSL's intended path is not to put the private text in Discord. OSL encrypts the payload locally, stores the encrypted payload in its relay, delivers an authenticated pointer through the OSL inbox, and types a wordbank cover carrying that pointer into Discord. Discord and anyone looking at the ordinary client see the cover row; an authorized local OSL receiver fetches, authenticates and decrypts the payload, then the eye paints the plaintext over that exact row without editing Discord's stored message. [SPEC 01-FULL-SPEC.md:9-15,33,41,69-82; CODE apps/osl-hub/src/discord_carrier_geometry.rs:1-31; CODE apps/osl-hub/src/broker.rs:4179-4221]

This is an overlay, not invisibility. With the eye closed, a person looking at Discord sees cover prose, its author, time and location. With the eye open, anyone who can see that local screen, screenshot, camera or screen share can see the plaintext. Discord sees and stores the cover and its ordinary account/channel/message metadata; its public policy says text messages are not end-to-end encrypted and that it collects messages and uploads. OSL must not say Discord cannot recover the private text until a real test proves that plaintext never reached Discord and the cryptographic path worked. [SPEC 01-FULL-SPEC.md:41-45,69-75,82; [Discord privacy policy](https://discord.com/privacy); [Discord on text versus audio/video encryption](https://discord.com/safety/more-private-and-secure)]

The current carrier also leaks that a row exists and approximately how many rendered lines the private message occupies, because the cover is shaped to the plaintext row height. It does not deliberately match every private line length. [CODE apps/osl-hub/src/discord_carrier_geometry.rs:33-53]

The three required scopes

1. Direct message

What sending involves. The intended audience is one other Discord identity and one verified OSL friend. The existing DM is opened, OSL binds the selected friend, prepares one encrypted OSL payload and one pointer cover, then places that cover in the Discord composer. A Discord DM is a distinct channel type with a recipient; DM permissions, message requests and spam filtering can prevent or divert first contact. The existing OSL TEST 2 DM avoids inventing a new destination, but its exact two participants still have to be recorded before each run. [CODE apps/osl-hub/src/broker.rs:463-509; CODE apps/osl-hub-ui/src/main.ts:8656-8751; [Discord channel types](https://docs.discord.com/developers/resources/channel); [Discord message requests](https://support.discord.com/hc/en-us/articles/7924992471191-Message-Requests); RULING D38, RULINGS.txt:1915-1938]

Who sees what. The other DM participant and the sender see the cover in Discord. An OSL-authorized receiver should see plaintext only after local authentication/decryption and the eye toggle. A third signed-in owner account outside that DM must receive neither a matching protected row nor plaintext; TASK 0941 describes that necessary negative control, but a checked task is not proof that it ran successfully. [SPEC 01-FULL-SPEC.md:63-65,79-82; TASK 0941, todo/06-chat-apps.txt:485-497; CODE apps/osl-hub-ui/src/services.ts:547-553]

Does the strip/eye work? DM is the only scope the canonical native path actually models. Activation hard-codes ScopeKind::Dm, HubConversationKind::Dm and one participant; the UI accepts one personId; receive attribution has only self and peer; and the native code says a direct message is the only conversation kind OSL protects. Even DM remains built-but-unproven, and the current scope binding uses OSL service/account/person/scope data rather than a provider-authenticated Discord conversation ID. [CODE apps/osl-hub/src/broker.rs:463-509,2831-2864; CODE apps/osl-hub-ui/src/adapters.ts:545-553; CODE apps/osl-hub/src/main.rs:3691-3706; CODE apps/osl-hub/src/native_discord_adapter.rs:20160-20168; CODE apps/osl-hub-ui/src/services.ts:547-553]

Main failure modes. Wrong-account or wrong-DM binding can still look right because visible Discord labels are only user-confirmed context evidence, not provider-authenticated identity proof. The correct response to missing or ambiguous row evidence is refusal or “sent but unconfirmed,” never an automatic retry that could duplicate a message. [CODE apps/osl-hub/src/native_discord_adapter.rs:1-20,217-267,18534-18597; SPEC 01-FULL-SPEC.md:98,104]

2. Group DM / group chat

What sending involves. Discord models a group DM separately, with an owner and recipient list; it can contain up to ten members including the current user, and current members can add more people. OSL therefore needs a frozen recipient set for this send, a key/envelope for every intended OSL recipient, and one Discord carrier row that all group members can see. Adding or removing a member must change the next send's recipient set rather than silently reusing an old one. [Discord channel types](https://docs.discord.com/developers/resources/channel); [Discord group chats](https://support.discord.com/hc/en-us/articles/223657667-Group-Chat-and-Calls); TASKS 0940 and 0940b, todo/06-chat-apps.txt:459-483]

Who sees what. Every member of the group DM sees the cover, including members without OSL. Only the frozen authorized recipient set should decrypt. OSL cannot claim the whole group is protected because one receiver opened the message, and Discord's public material does not establish what history a newly added group member receives; that history behavior must be measured rather than guessed. [TASKS 0940 and 0940b, todo/06-chat-apps.txt:459-483; [Discord group chats](https://support.discord.com/hc/en-us/articles/223657667-Group-Chat-and-Calls)]

Does the strip/eye work? No, not as group protection in the current code. The designs and tasks name group chat, and the generic strip can display a room label and roster, but native activation still creates a one-peer DM and receive attribution cannot distinguish several authors. A group-shaped UI or a carrier posted in a group is not evidence of group encryption. [SPEC 01-FULL-SPEC.md:102-106; DESIGN design/UI-FINAL-INSTRUCTIONS/Strip.dc.html:525-529,682-690,828-836; CODE apps/osl-hub/src/broker.rs:463-509,2831-2864; CODE apps/osl-hub-ui/src/strip-state.ts:94-109,188-210; CODE apps/osl-hub-ui/src/discord-row-attribution.ts:1-2,76-80,113-151]

Main failure modes. The most likely false pass is encrypting for PTB only, posting one cover to the three-account group, and declaring success when PTB decrypts while Canary cannot. TASK 0940b correctly requires that exact failure to be detectable; the implementation does not currently have the multi-recipient binding needed to pass it. Membership changes, several authors with similar display names, and one recipient using another recipient's OSL store are separate false-pass paths. [TASKS 0940 and 0940b, todo/06-chat-apps.txt:459-483; CODE apps/osl-hub/src/broker.rs:463-509; CODE apps/osl-hub-ui/src/discord-row-attribution.ts:113-151]

3. Server text channel

What sending involves. A server channel is a many-viewer destination governed by VIEW_CHANNEL, SEND_MESSAGES and READ_MESSAGE_HISTORY, with role/member overrides. OSL first needs an explicit encrypted audience: all approved OSL identities in the channel, or a smaller named subset. The spec and current code do not settle that choice. Until it is settled and bound to the actual guild/channel, posting a one-friend cover into a server is not protected server messaging. ([Discord permissions](https://docs.discord.com/developers/topics/permissions); SPEC 01-FULL-SPEC.md:102-106; CODE apps/osl-hub/src/broker.rs:463-509; CODE apps/osl-hub/src/main.rs:3691-3706)

Who sees what. Everyone with channel visibility sees the cover, including moderators, bots and non-OSL members. Discord sees the cover and metadata. Only the explicitly encrypted audience should see plaintext locally. In testing, a channel is usable only if every person who can view it is one of the owner's Stable/PTB/Canary identities; otherwise sending would message a real third party and is forbidden. [RULING D38, RULINGS.txt:1915-1938; [Discord permissions](https://docs.discord.com/developers/topics/permissions); SPEC 01-FULL-SPEC.md:41-45,69-75]

Does the strip/eye work? No server-channel claim is justified. The design shows #general, and plan tasks classify server/channel/thread surfaces, but those tasks do not prove protected delivery. Worse, the generic composer locator accepts Message #private while native encryption remains a one-friend DM. The strip can therefore mount in a channel and post a row without proving the intended channel audience. [DESIGN design/UI-FINAL-INSTRUCTIONS/Strip.dc.html:525-529; TASKS 0929 and 0936-0938, todo/06-chat-apps.txt:375-385,499-533; CODE apps/osl-hub/src/native_discord_adapter.rs:319-394,26903-26918; CODE apps/osl-hub/src/broker.rs:463-509]

Main failure modes. Missing permission, slow mode, AutoMod and spam classification can block or delay the row. Discord AutoMod applies to server text, threads and voice text and can match words, links, emoji, punctuation, symbols, mention spam and machine-classified spam. The code has no Discord-specific diagnosis for these cases; it can only prove a row or return failure/uncertainty. [RULING D14, RULINGS.txt:1124-1142; [Discord AutoMod](https://support.discord.com/hc/en-us/articles/4421269296535-AutoMod-FAQ); [Discord channel slow mode](https://docs.discord.com/developers/resources/channel); CODE apps/osl-hub/src/native_discord_adapter.rs:217-267,18534-18597]

Cross-scope breakage

BreakWhat is establishedProduct decision
Message lengthDiscord's ordinary user baseline is 2,000 characters; Nitro can raise it, and long pasted text can become a file. OSL independently caps a carrier at 2,000 characters/UTF-16 units and 8,000 UTF-8 bytes. A payload needing multiple pointer tokens gets no Discord carrier and falls back to the protected viewport. ([Discord sending limits](https://support.discord.com/hc/en-us/articles/360034632292-Sending-Messages); CODE apps/osl-hub/src/discord_carrier_geometry.rs:46-53,273-281; CODE apps/osl-hub/src/native_discord_adapter.rs:1492-1499; CODE apps/osl-hub/src/broker.rs:4184-4195)Keep the 2,000-character carrier ceiling and refuse/fallback. Do not use Nitro as the baseline and do not let Discord convert a protected body into an unprotected .txt upload. [CODE apps/osl-hub/src/discord_carrier_geometry.rs:136-160; SPEC 01-FULL-SPEC.md:72]
Carrier preservationOwner rulings identify autocorrect, capitalization, emoji substitution, Markdown, whitespace collapse, smart quotes and truncation as pointer-breaking transformations. The current wordbank format permits space-to-newline shaping and decodes by whitespace, but exact stored readback in Discord has not been measured. [RULING D30(c), RULINGS.txt:1676-1685; RULINGS D33/D33a, RULINGS.txt:1744-1809; CODE apps/osl-hub/src/discord_carrier_geometry.rs:10-18]Keep inert ASCII words, single spaces/newlines and no URL/emoji/Markdown. Require exact sent-versus-stored carrier comparison; if it differs, refuse and show the cause. Defer automatic repair until ordinary sending works. [TASKS 7230 and 7241, todo/40-cover-integrity.txt:50-60,310-330]
MarkdownDiscord renders Markdown, while the OSL protected transcript inserts plaintext with textContent. The geometry planner rejects rows marked as Markdown/media/reply; its native detector treats `* _ ~ backtick > # \[ ] as Markdown signals. ([Discord Markdown](https://support.discord.com/hc/en-us/articles/210298617-Markdown-Text-101-Chat-Formatting-Bold-Italic-Underline); CODE apps/osl-hub-ui/src/discord-protected-transcript.ts:206-208; CODE apps/osl-hub/src/discord_carrier_geometry.rs:67-76,136-160,213-240; CODE apps/osl-hub/src/native_discord_adapter.rs:1501-1545`)Plain text is the first working product. Do not claim Discord Markdown fidelity. Fall back for rich rows; test formatting later. [RULING D33, RULINGS.txt:1744-1787]
AttachmentsThe higher-authority spec says up to 16 files, Free 25 MB each and Pro 1 GB each, while stale TASK 0930 says 8 MB. Protected files are meant to travel through OSL; Discord should contain only cover, and the current overlay attachment path emits no Discord carrier marker. [SPEC 01-FULL-SPEC.md:71; TASK 0930, todo/06-chat-apps.txt:387-397; TASK 0933, todo/06-chat-apps.txt:423-433; CODE apps/osl-hub-ui/src/overlay.ts:1411-1465,1982-2006]Follow the spec, not the 8 MB task. Never attach the original file to Discord and call it protected. For first scope proof, test text only; test the OSL file path separately. [SPEC 01-FULL-SPEC.md:71; TASK 3429, todo/11-typing-into-other-apps.txt:419-438]
Embeds and link previewsDiscord stores message content and embeds separately. A link causes Discordbot to fetch preview data and may cache media; official material does not say it rewrites the body. Current carrier rules forbid URLs, and wordbank prose should create no preview, but this has not been live-proven. ([Discord message model](https://docs.discord.com/developers/resources/message); [Discord link previews](https://support.discord.com/hc/en-us/articles/42500550752919-About-Discord-Link-Previews-and-the-Discordbot); RULING D33, RULINGS.txt:1744-1787)Keep URLs out of carriers. Assert zero generated embeds on the test row and exact raw content. Later, regress a protected row adjacent to a normal linked message so preview DOM cannot confuse row selection. [TASK 3429, todo/11-typing-into-other-apps.txt:419-438]
Rate limits and slow modeDiscord documents changing API limits and 429 handling, but no stable numeric limit for ordinary desktop-user sends. Server channels/threads can impose slow mode up to six hours. The native desktop path does not use Discord's API and has no scope-specific rate-limit handler. ([Discord rate limits](https://docs.discord.com/developers/topics/rate-limits); [Discord channel resource](https://docs.discord.com/developers/resources/channel); CODE apps/osl-hub/src/native_discord_adapter.rs:1-20)Do not invent a numeric desktop limit. Make the first product send exactly one Discord row. On uncertainty, stop and ask the tester to inspect; never auto-retry a send that may already have landed. [CODE apps/osl-hub/src/native_discord_adapter.rs:217-267,18534-18597]
AutoMod and spamServer AutoMod sees the cover because that is Discord plaintext; Discord's DM spam systems may divert a message. No official classifier guarantee makes a wordbank carrier safe. [RULING D14, RULINGS.txt:1124-1142; [Discord AutoMod](https://support.discord.com/hc/en-us/articles/4421269296535-AutoMod-FAQ); [Discord safer messaging](https://support.discord.com/hc/en-us/articles/115000068672-Safer-Messaging-on-Discord)]Measure with real owner-only DMs/channels. A blocked or diverted carrier is a product failure, not a safety-policy reason to stop development; report the actual Discord state and adjust the carrier. [RULING D33a, RULINGS.txt:1789-1809]

Fourth scope: threads, then forum replies; defer voice text

Threads are the fourth scope worth having because the written spec already names them and the plan already has a distinct thread classifier. Public threads inherit parent-channel visibility; private threads add invitations; sending uses a separate permission; auto-close/lock/reopen changes whether the composer exists. That is close enough to server-channel work to reuse it, but different enough that a server-channel pass cannot prove a thread. [SPEC 01-FULL-SPEC.md:102; TASK 0938, todo/06-chat-apps.txt:521-533; [Discord threads](https://support.discord.com/hc/en-us/articles/4403205878423-Threads-FAQ); [Discord permissions](https://docs.discord.com/developers/topics/permissions)]

Forum posts are thread-backed, but creation adds title, initial-body and sometimes tag fields. First make replies in an existing owner-only forum thread work; defer protected thread creation until the ordinary channel/thread carrier works. Voice-channel text is another server message surface with different pop-out/sidebar geometry and transient previews, so keep it as a later regression rather than a fourth launch claim. ([Discord forum channels](https://support.discord.com/hc/en-us/articles/6208479917079-Forum-Channels-FAQ); [Discord voice-channel text](https://support.discord.com/hc/en-us/articles/4412085582359-Text-Channels-Text-Chat-In-Voice-Channels))

Code comparison

The canonical source inspected was local branch integration/full at commit 4d13b8a04f48767eb9c72c2432ae9ef01caf814a, as required by RULING D29. The working checkout was not treated as canonical. [RULING D29, RULINGS.txt:1620-1647; CODE commit 4d13b8a04f48767eb9c72c2432ae9ef01caf814a]

What exists

What does not exist

Discord Rust files inspected

The Discord-specific Rust set was: discord_carrier_geometry.rs, discord_product_observation.rs, discord_qa_identity.rs, discord_qa_identity_core_shim.rs, discord_qa_inbound_receipt.rs, discord_read_only_bridge.rs, discord_receive_wakeup.rs, discord_release_gate.rs, discord_send.rs, discord_typing_box_check.rs, native_discord_adapter.rs, native_discord_overlay.rs, task_0924_discord_message_rules.rs, task_3311_ordinary_discord_timer.rs, and native_discord_adapter/guided_deletion.rs. The decisive production path is broker.rs plus main.rs, even though their filenames do not contain discord. [CODE apps/osl-hub/src/*discord*.rs; CODE apps/osl-hub/src/native_discord_adapter/guided_deletion.rs; CODE apps/osl-hub/src/broker.rs:463-509,4179-4221; CODE apps/osl-hub/src/main.rs:3691-3706,4091-4145]

discord_typing_box_check.rs is only a read-only DM diagnostic: it accepts one foreground Message @person composer and deliberately refuses #. discord_release_gate.rs is a boolean gate whose own header says unit/loopback is not a substitute for a signed Windows package and two identities. Neither is live proof. [CODE apps/osl-hub/src/discord_typing_box_check.rs:1-4,54-77,87-97,131-187; CODE apps/osl-hub/src/discord_release_gate.rs:1-12,14-34,65-100]

Testing workflow

No test below was run for this report. Stable, PTB and Canary are recorded as three live owner-controlled identities, but account availability is only a precondition, never delivery proof. Only those identities may be used; abort before sending if any destination contains another person. [RULING D59, RULINGS.txt:2692-2716; TASK 0900, todo/06-chat-apps.txt:6-18; RULING D38, RULINGS.txt:1915-1938]

Evidence rules for every live test

1. Before opening OSL, record the build commit/package, Discord release channel, Discord account ID, distinct %APPDATA% store, distinct OSL data directory, exact conversation and scope, and every participant/viewer. Record one fresh random marker outside OSL and a zero-before count for that marker. [TASK 0915, todo/06-chat-apps.txt:188-198; TASK 0940, todo/06-chat-apps.txt:459-471]

2. Use the shipping UI and production send/receive path. Keep the receiver untouched until the sender has one Discord row. Record the provider message ID, exact raw stored cover, sender account and destination; zero rows or more than one row is failure. [TASK 3429, todo/11-typing-into-other-apps.txt:419-438; TASK 4150, todo/13-receive.txt:2744-2762]

3. With the receiver eye closed, require the live Discord cover and no plaintext. Toggle the eye by click, require the exact precommitted marker, then close it and require the cover again. The eye is a toggle, not press-and-hold. [RULING D11, RULINGS.txt:1035-1048; DESIGN design/UI-FINAL-INSTRUCTIONS/Strip.dc.html:828-836; SPEC 01-FULL-SPEC.md:79-82]

4. Compare the raw sent and stored cover exactly, assert the private marker never appears in Discord content, attachments, embeds, replies or logs, and retain the receiver's authenticated decrypt result. A screenshot or internal receipt alone is not proof. [RULINGS D33/D33a, RULINGS.txt:1744-1809; TASK 3429, todo/11-typing-into-other-apps.txt:419-438; SPEC 01-FULL-SPEC.md:98,108]

A. One-party local encode/decode diagnostic

The uncomfortable fact is that the requested one-account “send to self” is not a native Discord flow today. Discord has no established self-DM in these sources, native activation requires one different verified friend, and OSL's local loopback explicitly sets personToPersonE2ee: false. This test can prove local encode/decode plumbing only; it cannot prove Discord transport or E2EE. [CODE apps/osl-hub/src/broker.rs:463-509; CODE apps/osl-hub-ui/src/adapters.test.ts:214-227; CODE apps/osl-hub/src/discord_release_gate.rs:1-12; SPEC 01-FULL-SPEC.md:108]

Exact diagnostic:

1. Start one shipping OSL instance with a fresh data directory and Discord Stable signed into one owner account. Record the build, account and marker. Do not open or message any conversation. [RULING D38, RULINGS.txt:1915-1938; TASK 0900, todo/06-chat-apps.txt:6-18]

2. Invoke the production local-protected-loopback prepare path with the marker. Persist the returned local message/capsule, then pass that persisted result through the normal local parse/decode path rather than handing the original plaintext to the display. [CODE apps/osl-hub-ui/src/adapters.test.ts:214-227]

3. Pass only if the decoded bytes equal the precommitted marker, the persisted local message ID is the one opened, and the result still reports local_protected_loopback and personToPersonE2ee: false. [CODE apps/osl-hub-ui/src/adapters.test.ts:214-227]

False pass: using the original plaintext buffer; decoding a fixture or saved capsule; showing a prepared cover without reopening persisted state; posting no Discord row but calling it Discord proof; or labeling the result E2EE. [CODE apps/osl-hub-ui/src/adapters.test.ts:214-227; SPEC 01-FULL-SPEC.md:108]

Do not build a special Discord self-send feature before the two-account DM works. It buys less product evidence than the already available Stable-to-PTB test. [RULING D59, RULINGS.txt:2692-2716; TASK 0915, todo/06-chat-apps.txt:188-198]

B. Two-party direct message

1. Start Stable and PTB with their recorded owner accounts, separate Discord stores and separate fresh OSL data directories. Open the existing OSL TEST 2 DM in both. Verify it has exactly those two owner identities; abort if it has anyone else. [RULING D38, RULINGS.txt:1915-1938; TASKS 0900/0915, todo/06-chat-apps.txt:6-18,188-198]

2. Record the exact two account IDs, both store/data paths, DM identity, zero-before row count and fresh marker. Keep PTB's eye closed and do not touch its OSL receive UI. [TASK 0915, todo/06-chat-apps.txt:188-198; TASK 4150, todo/13-receive.txt:2744-2762]

3. On Stable, select PTB as the verified friend, open protection in that exact DM and send the marker once through the shipping control. Require exactly one new Discord row and read its raw stored cover back from the live client. [CODE apps/osl-hub-ui/src/main.ts:8656-8751; CODE apps/osl-hub/src/main.rs:4091-4145; TASK 3429, todo/11-typing-into-other-apps.txt:419-438]

4. On PTB, require one matching protected row with cover visible, click the eye, and compare the decrypted bytes to the precommitted marker. Close the eye and require the same cover. Keep Canary's receive job running as a bystander negative control and require zero matching rows/plaintext there. [TASK 0941, todo/06-chat-apps.txt:485-497; RULING D11, RULINGS.txt:1035-1048]

False pass: cover posted but no PTB decrypt; OSL inbox delivery without a Discord row; same OSL directory or account on both sides; replayed row/marker; fixture/QA route; wrong DM; more than one carrier; internal “sent” status without raw row readback; Canary can decrypt; or plaintext appears anywhere in Discord. [TASKS 0915/0941, todo/06-chat-apps.txt:188-198,485-497; TASK 3429, todo/11-typing-into-other-apps.txt:419-438; CODE apps/osl-hub/src/discord_send.rs:1-7]

C. Two-party server channel

This test is blocked by product code, not by consent process. Run it only after native activation binds the real guild/channel and an explicit recipient set instead of hard-coding a one-peer DM. Do not count the current generic #channel locator as support. [CODE apps/osl-hub/src/broker.rs:463-509; CODE apps/osl-hub/src/main.rs:3691-3706; CODE apps/osl-hub/src/native_discord_adapter.rs:319-394,26903-26918]

1. Use or create no destination blindly. Select an already existing private owner-controlled test channel, enumerate every identity with VIEW_CHANNEL, and continue only if every viewer is Stable, PTB or Canary. Record guild ID, channel ID, roles/overrides, recipient set, AutoMod state and slow-mode value. If no such channel exists, the test remains not run. [RULING D38, RULINGS.txt:1915-1938; [Discord permissions](https://docs.discord.com/developers/topics/permissions); [Discord channel resource](https://docs.discord.com/developers/resources/channel)]

2. Start Stable sender and PTB receiver with separate stores/data paths. Precommit a marker and zero-before counts. Bind protection to the recorded guild/channel and PTB recipient, not merely to the visible #name. [TASK 0915, todo/06-chat-apps.txt:188-198; SPEC 01-FULL-SPEC.md:102-104]

3. Send once from Stable. Require exactly one live row in that server channel, exact raw cover preservation, the correct author, no plaintext/attachment/embed, and no row in OSL TEST 2 or another channel. Record any Discord permission, AutoMod or slow-mode refusal as the result rather than retrying. [TASK 3429, todo/11-typing-into-other-apps.txt:419-438; CODE apps/osl-hub/src/native_discord_adapter.rs:217-267; RULING D14, RULINGS.txt:1124-1142]

4. On PTB in the same channel, require cover with eye closed and exact marker with eye open. If Canary was included in the encrypted audience, it must independently decrypt; if excluded but still a channel viewer, it must see cover and get zero plaintext. [SPEC 01-FULL-SPEC.md:79-82,102-104; TASKS 0940/0941, todo/06-chat-apps.txt:459-497]

False pass: a DM carried the message; only the surface classifier or strip mounted; the room was identified by mutable label alone; encryption still targeted one manual friend while claiming channel-wide protection; OSL inbox delivered but Discord did not; any real third party could view the channel; only the first chunk/row was checked; or an uncertain send was retried. [TASKS 0929/0937, todo/06-chat-apps.txt:375-385,511-521; CODE apps/osl-hub/src/broker.rs:463-509; CODE apps/osl-hub/src/native_discord_adapter.rs:217-267]

D. Group chat

This test is also blocked until the product supplies a frozen multi-recipient set and multi-author attribution. The plan says the existing group contains Stable, PTB and Canary; verify that live before sending because the hard line is owner accounts only. [TASK 0940, todo/06-chat-apps.txt:459-471; RULING D38, RULINGS.txt:1915-1938; CODE apps/osl-hub/src/broker.rs:463-509,2831-2864]

1. Open the existing three-owner-account group DM in Stable, PTB and Canary. Record group identity, exactly three account IDs, three Discord stores, three OSL data directories, the frozen recipient set {PTB, Canary}, zero-before counts and a fresh marker. Abort if any other member is present. [TASK 0940, todo/06-chat-apps.txt:459-471; RULING D38, RULINGS.txt:1915-1938]

2. From Stable, send once through the shipping protected control. Require one provider message ID and exactly one carrier row, not one row per receiver. Compare the stored cover exactly and require closed eyes on PTB and Canary to show only that cover. [TASK 0940, todo/06-chat-apps.txt:459-471; RULING D33a, RULINGS.txt:1789-1809]

3. Independently click the eye in PTB and Canary. Each must fetch/authenticate/decrypt through its own OSL data directory and produce the same exact marker. Record each result separately. [TASK 0940, todo/06-chat-apps.txt:459-471]

4. Run the negative control in a throwaway build or injectable recipient-set test: omit Canary while leaving PTB intact, then require PTB to pass and Canary to stay at zero. Restore the shipping build and repeat with a fresh marker. [TASK 0940b, todo/06-chat-apps.txt:473-483]

False pass: PTB decrypts but Canary is never checked; one receiver/store is used twice; sender acts as a receiver; separate carrier rows are sent per recipient; a saved marker/row is replayed; membership is not frozen; or a fixture says Canary was included while its real receiving job was absent. [TASKS 0940/0940b, todo/06-chat-apps.txt:459-483]

Product-first cuts and keeps

Cut TASK 4153's requirement for a non-owner third party to install, send and reply. It directly violates the non-negotiable “never message a real third party” line and buys usability research, not core transport proof. [TASK 4153, todo/13-receive.txt:2804-2813; RULING D38, RULINGS.txt:1915-1938]

Split TASK 7761. The first Discord release gate should require only: shipping build; exact owner account and destination; no plaintext to Discord; one exact stored cover; one other owner identity authenticates and decrypts the precommitted marker. Reveal polish, view-once, timers, burn, attachments, long-message fidelity, restart and stress belong in later gates and must not be advertised until each is proven. The current bundled gate slows the first working message without preventing the two harms that matter here. Its evidence already says the package and two identities were not observed, despite the task being checked. [TASK 7761, todo/48-carriers-and-mail.txt:44-60; EVIDENCE evidence/7761.md:1-9,83-102; CODE apps/osl-hub/src/discord_release_gate.rs:1-12,65-100; SPEC 01-FULL-SPEC.md:98,108]

Keep exact destination/account binding, zero plaintext fallback, exact stored-carrier readback and a real recipient decrypt. These stop a user being harmed or stop OSL shipping a lie. Keep visible refusal when Discord drift prevents proof; defer automatic self-repair and visual tutorial polish until the DM round trip works. [SPEC 01-FULL-SPEC.md:15,69-75,98,104; RULING D33a, RULINGS.txt:1789-1809; TASKS 6884/6885, todo/32-final-ui-spec.txt:150-184]

The shortest honest build order is: local loopback diagnostic, real Stable-to-PTB DM, channel ID/recipient binding plus owner-only server test, multi-recipient group DM, then thread reply. Nothing should be called supported before its exact live test passes. [SPEC 01-FULL-SPEC.md:98,102-108; CODE apps/osl-hub-ui/src/services.ts:547-553]