mChat Français Open mChat

mChat · document

Encrypt from your own code

The SDK runs on your side, in the browser or under Node. The relay carries envelopes it cannot open. There is nothing to install and no API key to request.

01

There is no route that encrypts

That is deliberate, and it is the one design decision that matters here. A route that encrypts receives your message in the clear: the server could read it, therefore keep it, therefore be compelled to hand it over. Calling /api/v1/chiffrer returns a 410 that repeats this sentence, because it is the first address everyone tries.

02

Sealing a message

The SDK imports straight from the domain, with nothing to install: everything it uses is served alongside it. Under Node, import /sdk/mchat.mjs instead, which keeps its dependencies as bare specifiers.

// navigateur : rien a installer, aucune cle d'API
import { genererIdentite, sceller, ouvrir }
  from 'https://medchat.medcode.ca/sdk/mchat.web.mjs';

const bob = genererIdentite();

// Seal with the recipient's PUBLIC keys
const enveloppe = sceller(bob.publiques, 'The code is 4417');

// Only they can open it
ouvrir(bob.privees, bob.publiques, enveloppe);
// -> 'The code is 4417'
03

What the seal does

An ephemeral X25519 pair, thrown away immediately, plus an ML-KEM-768 encapsulation to the recipient's key. The encryption key derives from both secrets at once and closes over ChaCha20-Poly1305. Both halves must break: one holds against today's computers, the other against what comes next.

04

Under Node

The primitives are public packages: install them once, fetch the SDK next to them, and the same code runs without a browser. This is where the bare specifier form belongs, not the web one.

npm i @noble/curves @noble/post-quantum \
      @noble/ciphers @noble/hashes
curl -O https://medchat.medcode.ca/sdk/mchat.mjs
05

Depositing and collecting, for real

Collecting your queue needs an ed25519 signature over one exact string. It is the only part you can get wrong in silence: a malformed signature returns 403 without saying which half was wrong.

import { genererIdentite, sceller, ouvrir } from './mchat.mjs';
import { ed25519 } from '@noble/curves/ed25519.js';

const API = 'https://medchat.medcode.ca';
const post = (c, b) => fetch(API + c, { method: 'POST',
  headers: { 'content-type': 'application/json' },
  body: JSON.stringify(b) }).then(r => r.json());

const me = genererIdentite();
await post('/api/v1/identite', { nom: 'my_service', publiques: me.publiques });

// write to someone
const { publiques } = await fetch(API + '/api/v1/identite/bob').then(r => r.json());
await post('/api/v1/relai', { pour: 'bob', enveloppe: sceller(publiques, 'hello') });

// collect YOUR queue: signature required
const ts = Math.floor(Date.now() / 1000);
const msg = new TextEncoder().encode(`mchat-api|relever|my_service|${ts}`);
const sig = Buffer.from(
  ed25519.sign(msg, Buffer.from(me.privees.sig, 'base64'))).toString('hex');

const { enveloppes } = await post('/api/v1/relever', { nom: 'my_service', ts, sig });
for (const e of enveloppes) console.log(ouvrir(me.privees, me.publiques, e));
06

The seal does not authenticate the sender

Anyone who knows a public key can seal a message for it: that is the property of a sealed box, not a flaw. If your recipient needs to know who wrote, sign the plaintext before sealing it.

const sig = signer(alice.privees, message);
const env = sceller(bob.publiques, JSON.stringify({ message, sig }));

// on Bob's side
verifier(alice.publiques, message, sig); // true
07

Publishing keys and collecting mail

The relay is optional: you can carry envelopes by your own means. If you do use it, collecting your queue requires a signature, otherwise knowing a name would be enough to empty somebody else's mailbox.

// resume des routes
// publish your PUBLIC keys under a name
POST /api/v1/identite      { nom, publiques }
GET  /api/v1/identite/:nom

// deposit and collect
POST /api/v1/relai         { pour, enveloppe }
POST /api/v1/relever       { nom, ts, sig }

// sig = ed25519 over: mchat-api|relever|<nom>|<ts>
// ts in seconds, 120 s window
08

The limits, written down in advance

An envelope is at most 64 KiB and a queue at most 200 waiting envelopes: beyond that a deposit is refused with a 429 rather than taking the service down. Queues are written to disk and survive a restart; an envelope left uncollected for 30 days is dropped. A name already taken is not overwritten without the key that published it.