SolarOS

Communications · Tutorial

Use Sealed Chat (SC2)

Sealed Chat brings encrypted direct messages and private group rooms to SolarOS. You use the same Chat app, conversation history, and Outbox as the other messaging providers. The sc2-sync background job connects your device to a relay and moves messages while Chat is closed.

This tutorial takes two SolarOS devices from relay setup to a verified direct conversation, then shows the room administrator and member sides of a private room. Commands in text blocks run at the SolarOS shell. Commands beginning with / run in Chat. Replace uppercase placeholders with your actual values; the example domain relay.example.org is not a public service.

Understand the three conversation types

ConversationWho can read the text?How you open it
Direct messageYour device and the addressed peer devicemessages query sealed PEER_DEVICE_ID
Private roomDevices admitted to the room and given its keysCreate a room or join with its secret invite
Public roomThe relay and people with access to that roommessages join sealed pub:general

Direct messages and private-room text are encrypted on the sending device and decrypted on the receiving devices. The relay routes and stores encrypted messages, but still sees connection and routing information. Encryption does not hide your network address or room membership from the relay.

Public-room posts are signed, readable messages. Their signatures identify the signing device key; public rooms are unsuitable for private information. TLS protects the connection to the relay and complements message encryption.

An sc2 instance uses the SC2 protocol. The separate gateway provider uses legacy v1, even when the two services share a hostname or port. Select sc2 for the examples below.

Prepare both devices

These examples describe the current sc2_dev firmware, including named rooms, sender labels, and explicit job startup. Use recent matching SC2 firmware on both devices, especially for private-room admission. Check availability:

apps
jobs
messages classes

You need the Chat app, the sc2 class, and the sc2-sync job. Connect Wi-Fi, set the clock for TLS, and mount writable storage for connection profiles and history. The quick start covers those steps. Use a full SC2 relay for private rooms; the device's smaller chatd relay supports only DMs and the public room pub:general.

Give each device a readable identity. For example, on one device:

identity user alice
identity hostname terminal-a
sc2 id
sc2 status

On the other, choose a different hostname, such as terminal-b. Updated clients include the SolarOS label, for example alice@terminal-a, with their messages.

The device: line from sc2 id is a 32-character hexadecimal cryptographic identity. Exchange that ID when opening a DM. A displayed name is a label; it does not replace the device ID or safety-number check. Each device must have its own keys. Changing identity user or identity hostname changes the label, not the SC2 device key.

Add your relay

Use the hosted SolarOS relay below, or obtain your own relay's SC2 endpoint and any admission code from its operator. Choose one setup method and use the same relay on both devices. sealed below is a local instance name you choose.

Connect to the hosted SolarOS relay

An active SC2 relay is available at chat.solar-os.eu:1337 over TLS. Add it on each device:

messages add sc2 sealed chats://chat.solar-os.eu:1337

No key= argument is required for this direct TLS connection. SolarOS validates the server's TLS certificate and hostname. Continue with the enable and worker-start commands below. This endpoint also accepts legacy v1 clients; the sc2 provider selects Sealed Chat.

Use a domain with signed discovery

For another relay whose operator publishes a signed descriptor, run:

messages add sc2 sealed relay.example.org key=BASE64_RELAY_KEY

SolarOS fetches the descriptor over verified HTTPS and saves its SC2 endpoint and relay key. The key= value checks that the descriptor names the key you received from the operator. If an admission code is required, place it between the domain and the key:

messages add sc2 sealed relay.example.org ADMISSION_CODE key=BASE64_RELAY_KEY

Choose the appropriate command once. If the operator does not supply a key, domain discovery can obtain it from the HTTPS descriptor; omit key=... in that case.

Or use an explicit TLS endpoint

Use this alternative when the operator gives you a host and port instead of HTTPS discovery:

messages add sc2 sealed chats://relay.example.org:7778

Replace the port with the operator's SC2 port. Append an admission code if required. This creates the same kind of instance; do not run it as a second setup command with the same name.

chats:// uses TLS. Without a certificate pin, the certificate must validate against the device's CA bundle. Optionally append key=BASE64_RELAY_KEY to pin the relay's SC2 signing key as an additional identity check. Obtain that key from the operator; it is separate from the optional TLS certificate pin in a #pin=sha256/... URL. Direct URL setup without key= does not save an SC2 signing-key pin. Domain discovery saves the descriptor's relay key even when you omit key=. Message encryption and peer safety-number verification work with either setup method. Use the operator's exact settings for a pinned or self-signed endpoint. The messaging reference explains both pins.

Enable the instance and start its worker

New instances start disabled. Run these commands on both devices:

messages connect sealed
job start sc2-sync
job status sc2-sync
sc2 relay
messages instances

Wait for sc2 relay to report a ready connection. A running job alone does not prove that the relay login succeeded. Correct an admission code with messages configure sealed admit ADMISSION_CODE if needed.

Enabling, configuring, restoring an instance, or sending a message does not start the job. Messages queue while the worker is stopped. Closing Chat leaves the job running.

The worker currently serves the first enabled SC2 instance. Keep one enabled for this tutorial; creating several instances does not create several simultaneous connections. Use messages disconnect OTHER_NAME to disable an instance you are not using.

Try the public room and Chat controls

On both devices:

messages join sealed pub:general
messages conversations
chat sealed

pub:general is the public-room address; Chat may display it as #general. Select it and send a short, non-sensitive greeting. Check that the other device receives the message. Chat marks public SC2 rooms (public) and explains their signed, public nature when first opened.

ControlAction
TabSwitch between Channels and Chat
Up / Down in ChannelsSelect a conversation
Enter in ChannelsOpen the selected conversation
Enter in ChatSend text or run a slash command
Page Up / Page DownScroll retained messages
Esc or /quitExit Chat

Inside Chat, /join sealed pub:general opens the same public room. Use /status to inspect state. The shell's sc2 command also supports Tab completion for subcommands, stored peers, aliases, and private-room names.

Open and verify a direct conversation

Obtain the other device's full device: ID from sc2 id. At the shell:

messages query sealed PEER_DEVICE_ID
messages conversations
chat sealed

Select the new DM and exchange a harmless greeting so the clients fetch and store each other's keys. Inside Chat, /query sealed PEER_DEVICE_ID opens the same conversation. The peer must have connected to the relay and published its keys before a new session can be established.

Before sharing private information, compare safety numbers over a channel you already trust, such as in person. With the DM selected, enter:

/safety

Compare the displayed code, words, and full safety number with the other device. If they match, press V to mark the peer verified. U clears that mark. The DM header shows (verified) or (unverified).

The shell equivalents are:

sc2 safety PEER_DEVICE_ID
sc2 verify PEER_DEVICE_ID
sc2 unverify PEER_DEVICE_ID

Run verify only after the comparison succeeds. If the numbers differ, check the device IDs together before continuing. Encryption and a readable sender label alone do not confirm that you chose the intended person.

For a local shortcut after the peer is known:

sc2 alias bob PEER_DEVICE_ID
sc2 safety bob
sc2 alias

For sc2 peer commands, aliases and unique known-ID prefixes of at least six hexadecimal characters can stand in for full peer IDs. An alias is local to your device; it does not rename the remote device for everyone else.

Create a private room: administrator

On the device that will administer the room, with a ready connection to the full relay's primary endpoint (check sc2 relay):

sc2 room create "Team room"
sc2 rooms

The creator becomes the room administrator. The command prints a stable room ID and a secret invite beginning with sc2i:. Share the complete invite privately with the intended member. Anyone holding it can request admission; do not post it in a public room.

Keep the administrator's device online with sc2-sync running while new members join. It checks the joining device's encrypted proof of invite possession, admits the member, and sends the room key and membership checkpoint. The relay alone cannot complete admission.

To print the invite again on the administrator's device:

sc2 room invite "Team room"

Room names may contain spaces and up to 48 UTF-8 bytes. Quote names containing spaces. The encrypted membership exchange gives the name to joining devices. The name is a display label; the room keeps its stable 32-hex ID. Invite lookup accepts a unique exact name, the room ID, or a unique ID prefix.

Join the private room: member

On the other device, enable and start the SC2 connection to the relay named in the invite, then run:

sc2 room join COMPLETE_SC2I_INVITE
sc2 rooms

Replace COMPLETE_SC2I_INVITE with the entire sc2i:... value you received. For an invite too long for the shell input line, save that text in a file on mounted storage and use its actual path:

sc2 room join @/sdcard/team.invite

waiting for an admin means the administrator has not yet admitted this device. waiting for the room key means the membership exchange is still in progress. Keep both workers online and wait for joined before sending. Both devices need firmware supporting this admission exchange; an older client may remain waiting.

Talk in the private room

On each device:

messages conversations
chat sealed

Select Team room. Send a message from each device and check that it appears on the other with its sender label, for example alice@terminal-a: hello. Older peers without sender labels fall back to a device-ID label.

For shell use, copy the decimal conversation ID printed by messages conversations on that device:

messages send ROOM_CONVERSATION_ID "Hello from the private room."
messages list ROOM_CONVERSATION_ID

The decimal conversation ID is local to that device. It is different from the stable 32-hex room ID and may differ between members. Use a room ID or name with sc2 room invite, and a local conversation ID with messages send, messages list, or chat.

Once members have their keys, existing members can exchange messages through the relay while the administrator is offline. New admissions still need the administrator online. Private rooms live on the primary relay; they do not move to fallback relays.

Leave a room or replace its invite

Leave from Chat with /leave, or from the shell:

messages leave ROOM_CONVERSATION_ID

Leaving deletes this device's local room record. An administrator who leaves loses the ability to print that room's invite or admit new members.

The device UI does not currently revoke invites or remove individual members. If an invite has been shared too widely or a member must lose access, create a new room and give its new invite only to the intended members.

Check delivery and restart behavior

messages outbox
messages list ROOM_CONVERSATION_ID
sc2 relay

queued means the local system accepted the message. sending means the transport is processing it. For SC2, sent follows relay acknowledgement; it is not confirmation that a person read the message. A successful send may leave Outbox quickly, so check conversation history for the final state.

Messages still waiting in the shared queue do not automatically survive a reboot. Review history and failed sends before resending. Closing Chat keeps the running worker and queue available.

Use job stop sc2-sync to stop the worker, and job start sc2-sync to resume. Use messages disconnect sealed to disable the instance. Enabled settings persist, but jobs need to be started again after reboot. To start the worker at boot, use setterm startup to find the selected startup script and add job start sc2-sync after its network setup. The background jobs guide explains startup and status.

The SC2 key store is kept in /.sc2. Keys, connection profiles, and retained history are not protected by message encryption on local storage. Protect physical and shell access; flash encryption is a separate device setting. Give replacement devices their own identity and verify them again.

Try a small SC2 relay on your LAN

For a DM or pub:general demonstration, a SolarOS device with SC2 support and PSRAM can host chatd:

job start chatd 7777 demo-token --v1 off
job status chatd

Copy the key= value from its startup log. On each client, use the server's actual LAN IP and that key:

messages add sc2 lan-sealed chat://SERVER_IP:7777 demo-token key=BASE64_RELAY_KEY
messages connect lan-sealed
job start sc2-sync
messages join lan-sealed pub:general
chat lan-sealed

Disable any other enabled SC2 instance first, so the worker selects this one. chat:// is plain TCP: private DM text is still encrypted, but TLS does not protect the surrounding connection or admission code. Use a trusted local network for this example.

chatd supports both v1 and SC2 by default on capable builds; --v1 off selects SC2-only operation. Its mini-relay has fixed device, mailbox, and history limits, and loses mailbox and public history when stopped. It does not support private rooms. Use a full SC2 relay for the group-room steps. See the chatd reference for its limits.

Diagnose a missing message

SymptomWhat to check
sc2 or sc2-sync is missingInstall firmware with the SC2 provider and worker; private-room peers need matching admission support.
Messages remain queuedEnable the intended instance, start sc2-sync, and inspect sc2 relay.
Job is running but the relay is not readyCheck Wi-Fi, clock, the exact SC2 port, TLS settings, and admission code.
HTTPS discovery is refusedConfirm the operator publishes a valid signed descriptor over verified HTTPS, or use their explicit SC2 endpoint.
Relay key differs from the pinConfirm a key change with the operator through a trusted channel. The client does not silently accept a replacement.
A DM fails with no peer keysCheck the full peer device ID and have that device connect to the same relay to publish its keys.
A private room is waitingCheck both firmware versions, both workers, and the administrator's primary-relay connection.
Private sends fail while on a fallbackPrivate rooms require the primary relay. Inspect its availability with the operator.
Messages show device IDs instead of namesCheck the sender's identity settings and firmware support for sender labels.
Logins repeatedly replace each otherTwo connections may be using the same device key. Keep one identity per physical device.

The messaging reference covers relay pins, fallback behavior, safety numbers, and protocol details. The general messaging tutorial covers Contacts, Inbox, Outbox, and the other providers.