Skip to content
Gryt-chatPublic

About

Gryt's shared application logic: one implementation of the things the desktop and mobile apps both do.

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Gryt logo

@gryt/core

The application logic from Gryt that the desktop and the phone both run.
One implementation of the things they were each doing separately.


npm install @gryt/core

The desktop app and the mobile app carried a copy each of the same decisions. Some were real copies, some were the same idea written twice, and nothing told the two apart until somebody diffed them by hand. report.ts had the same seven exports on both sides and a different MESSAGE_MAX on each, 8000 against 4000, with nothing recording why.

They run this, not two ports of it. Both apps should pin the same version: a shared package on two versions is two implementations again.

What goes in here

Two questions, and a module needs both answers to be yes:

  • Would it compile with no DOM and no React Native?
  • Would both apps otherwise need a copy?

tsconfig.json enforces the first by leaving DOM out of lib, so document, window and localStorage fail to typecheck rather than working in one app and breaking in the other. The second is a judgement call, and the surface guard is what stops it drifting: scripts/check-public-surface.mjs fails when an export goes missing and when one appears that nobody listed.

What it is

  • reports — the shape Gryt-chat/reports takes, and how an app fills it in. Diagnostics is the union of what each platform can find out, so a renderer sends its Chrome build, a phone sends whether it's a simulator, and a field neither fills can be seen to be dead.
  • links — which site a URL belongs to, what colour to draw its card, what line to read out of its path, and which of four shapes a preview earns. 73 sites, seven of which also read a detail from the path.
  • profile — the pool of placeholder names a person gets before they pick one. Both apps handed these out and both files said to keep the other in step by hand.
  • permissions — the channel scope matrix: rules in, grid out, and back. The two apps keyed their cell map differently, a NUL on the desktop and a space on the phone, which is a key one of them could collide on.
  • mls — the DM half of MLS stage 1. It keeps a device's KeyPackages topped up, opens a DM group, joins from a Welcome, sends, and reads the group log in order. For linking a device, addOwnDevice puts a new device of yours in every DM straight away and says where in each group's log it went in. groupPositions says where each log stood when the history snapshot was taken. Each app hands it the socket, the storage and the pins through the interfaces in src/mls/interfaces.ts. mlsPinsFromPeerPins builds the pins from the peer pins @gryt/crypto already keeps. encodeMlsDmContent and decodeMlsDmContent write and read what goes inside each message, so both apps use the same JSON: a message, an edit, a delete or a reaction. readMlsDmContent also says when something came from a newer app, so it can be skipped quietly rather than counted as broken. applyMlsReaction puts a reaction on a message in the archive, since the server never sees one. It depends on @gryt/crypto, pinned exactly the way the apps pin it.
  • pairing — linking a new device by QR code or short code, as docs/pairing-design.md in @gryt/crypto lays it out. There's one state machine per side. createNewDevicePairing shows the QR and code, checks the emoji, opens the envelope and, for an account, signs in with the device grant and checks the ID token is for the right person. It writes nothing until all of that passes. createApproverPairing claims the session, shows who's asking, seals the envelope, approves the sign-in through the Keycloak extension, and adds the new device to every DM. If the extension isn't there, it hands back Keycloak's own device page to open instead. The app passes in fetch, a clock, storage and the OIDC calls through src/pairing/interfaces.ts. When the app passes its archive, history goes across too. On Approve, A notes where each group's log stands, reads the archive newest day first through HistoryArchive, and seals and uploads it in chunks. The envelope carries the history key and the first chunks, and later sealed messages list the rest. After the adds, A sends the tail: what it decrypted between the snapshot and each add, plus old-epoch messages that land after it, which the app reports with noteMessage. N fetches the chunks newest first, checks each against its manifest entry, and hands each message to HistorySink once. Both sides report progress through subscribeHistory. It uses @noble/hashes for the PKCE challenge, which @gryt/crypto already pulls in.

What deliberately isn't

Anything needing a platform. Fetching is the clearest case: the desktop and the phone reach a server differently, so this decides what a link preview means and each app goes and gets it.

Artwork is the other one. A brand logo is a React component on the desktop and an SVG path or a favicon on the phone, so the package owns the hostnames, the colours and the path rules, and each app maps a provider id to its own icon.

@gryt/voice splits web and native behind two entry points because a media engine can't avoid the platform. This package has no such entry, on purpose. If one becomes necessary, that's a decision to make out loud rather than by adding DOM to the tsconfig.

Checks

npm test          # node --test, no runner to install
npm run typecheck
npm run build     # tsc to dist/, then rewrite specifiers for ESM
npm run check-surface

prepublishOnly runs the build, the tests and the surface guard, so a release that would have shipped a missing export fails before it leaves.

Issues

Please report bugs and request features in the main Gryt repository.

Sponsors

What sponsoring pays for, the tiers, and everyone who has sponsored: gryt.chat/sponsors. To sponsor: GitHub Sponsors.

The list itself lives in the Gryt README, in one place rather than ten, so it cannot fall out of step across repositories.

License

AGPL-3.0 — Part of Gryt

@gryt/ui is the exception in this org, and deliberately so: it's generic components with nothing of Gryt in them, and copyleft there would rule out most of the people who might use it.

This isn't that. It's the decisions the Gryt apps make, which is the product rather than scaffolding around it. It's still yours to embed, self-host and modify. The licence only bites for running a modified version as a closed service.

About

Gryt's shared application logic: one implementation of the things the desktop and mobile apps both do.

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages