Releases: ZahakJ/concord
Release list
Concord v0.58.0
Concord v0.58.0
Seventy-six commits, and most of them are visible. A guild can now carry the
history of a community that existed before it did. A hundred people can join
one at the same moment without the room grinding. The phone app has been
rebuilt from the message list outward. And every message that reaches you
through another member's copy of the history now proves who wrote it.
Bring a community's history with you
The thing people have asked for most is the thing a peer-to-peer app has the
hardest time doing: bringing a decade of somebody else's conversation with
them. Four hundred thousand messages sent as four hundred thousand messages
would go through the group ratchet one at a time and leave every member's
database the size of the export.
So they do not become messages. They become a chronicle — a guild's bulk
archive, signed by the guild's owner, that almost none of anybody ever
downloads.
What travels on the ordinary paths is a table of contents: the channels, the
authors, and one entry per page with its time range and its decryption key.
From that alone a member knows the archive exists, what is in it, and which
page holds any given day, without a byte of the archive itself. Pages are
fetched only when somebody scrolls that far back, and every member who has
read a page becomes a source for it — so the archive outlives the machine
that imported it. The standing cost on an idle guild is 28 bytes, because
an archive's id is already a hash of its own table of contents; four archives
cost 85.
The import wizard shows its arithmetic before it does anything. An import
is irreversible, takes minutes, and lands a decade of someone else's
community in front of every member of a guild. Asking anyone to commit to
that against a folder they downloaded eighteen months ago and have not opened
since is not a reasonable thing to ask. So the first half of the importer
only looks: it reads the export, counts everything in it, and shows you the
totals, every channel with its own message count and span and media size, the
size distribution of the attachments, and — plainly — how much media the
export named and did not actually bring. Then you narrow it, with a live
estimate under the controls that recomputes as you drag.
It never loads the file and it never fetches anything. A single exported
channel routinely runs to hundreds of megabytes and this has to survive on a
phone, so the scan is driven token by token: fifty megabytes of export scans
with the heap growing by eighteen kilobytes. And an export made without
assets points its attachments at somebody else's content network; following
those links would tell a third party the whole shape of what is being moved.
Those files are counted, named, and never opened — an archive says what it
has and what it does not, rather than leaving a reader to wonder whether 2014
had no pictures in it.
Where a channel used to say "this is the beginning", it now says what is
above it, and scrolling further reads it. On a metered connection nothing is
fetched at all, and an inline card offers to anyway.
Imported text is the only text in the app that nobody in the guild wrote or
read, so it gets a gate the live path does not have — not for HTML, which the
renderer already handles, but because a message body is where this
application carries structured content, and one of those forms is a
self-destruct. A single archived line carrying a past-dated ephemeral token
would delete itself out of every member's store. Those are renamed on the way
in: the text stays readable and every token is inert.
A hundred people can arrive at once
A guild used to pay for every joiner separately: propose, commit, publish,
welcome, repeat. The joiner barely notices — it is the people already in the
room who pay, because a commit must be applied gaplessly and in order by
everybody, rekeys the group, and strands any message in flight when it lands.
Whoever drops one of those then needs a repair, which is two more commits.
The bill for a join wave grew faster than the wave.
Admissions are collected now. Someone arriving at a quiet guild is still
committed immediately; while a commit is in flight, everyone else dialling in
waits in a queue that commits together. A wave of N costs one epoch per
wave, not one per joiner. Measured end to end with real peers over real
gossip:
| before | after | |
|---|---|---|
| 50 joiners at once, epochs | 50 | 3 |
| whole guild converged | 10.4s | 4.9s |
| 100 joiners in fours, epochs | 128 | 10 |
| whole guild converged | 2m16s | 47s |
The 128 is the number worth staring at. A hundred joiners produced a hundred
admission commits and twenty-eight more, because members could not keep up
with the churn the joins themselves caused. That is the superlinearity, and
batching removes it rather than merely reducing it.
Joining also got much smaller. The invite response carried every profile
the guild had ever learned, whole — avatars at 64 KiB, profile banners at 256
KiB, most of them belonging to people you will not look at today. What a
newcomer needs in the first second is names. The roster still carries
everyone's name, colour and mailbox key and drops the three fields that can
each outweigh all the rest put together. On a guild of fifty members with a
12 KiB avatar each that is 312,116 bytes down to 3,716 — and the pictures
arrive from their authors within seconds anyway, because every member
re-announces itself on meeting a peer it has not greeted.
And the commit log stops growing forever. It kept every membership change on
every device for the life of the guild; it now keeps the last two thousand
epochs or thirty days, whichever is more generous. The member who falls off
the end is not abandoned — they are routed to the re-add repair, which is
cheaper for everyone than replaying two thousand commits at them.
The phone app, rebuilt around your thumb
The message list draws what you are looking at. It used to render every
message it had ever loaded — 1,606 elements at two hundred rows, 3,693 after
paging back, 9,321 inside an imported archive — and nothing ever took any of
them away. It renders a window now, with spacers holding the height of
everything not drawn, so the scrollbar still describes the whole thread.
Rendered elements at 460 rows: 3,693 → 285. Deep in an archive: 9,321 →
154. Channel switch 118ms → 55ms with no long task, JS heap 52MB → 16MB.
Two decisions make that safe: the window is measured in pixels rather than
rows, so a row must be dragged a whole screen out of sight before it is
unmounted — which is what keeps a revealed spoiler or a played effect from
being thrown away under ordinary reading — and a message being edited is
pinned, because its editor commits on blur.
Half the boot payload is gone. Fifty dialogs and four cosmetic libraries
were in the first chunk the app fetched, so a session that opened no dialog
and looked at no decorated profile paid for all of it before the first
message appeared. Boot JS 1,226KB → 605KB. Fifty theme palettes were
downloaded to use one, every rule scoped to an attribute that is not on the
document unless you chose it: boot CSS 559KB → 288KB. The QR libraries
now load on the two screens that scan.
Every sheet answers a thumb the same way. There were three
implementations of one gesture and two surfaces that drew a grab handle and
wired nothing behind it. Worse, wiring them up found that the dialog sheets
could not be swiped away on Android at all — the grab strip handed every
downward drag to the browser as a scroll and the browser cancelled the
gesture on the second move. Forty-nine dialogs, none of them dismissible by
the gesture they advertised.
Back is Android's own gesture again. Three separate answers to "what is
open right now?" disagreed with each other, and seven things that visibly
cover the screen were registered in none of them. On a phone it was
reproducible and absurd: from a conversation with nothing open, back opened
the drawer, back said "press back again to exit", back closed the drawer,
back opened it again — eight presses at 900ms apart never left the app. There
is one navigation stack now, open order is the order, and the app tells
Android in advance which presses it wants, so the launcher can slide in under
a moving thumb the way it does everywhere else.
The rest of the phone work, briefly: typing no longer forces three layouts
per keystroke (3.0 → 0.98); dragging a drawer went from 31.2% janky frames to
9.9% and 109 repaints per traversal to 16; the hover toolbar is no longer
built on devices that cannot hover, taking a 200-row channel from 8,358
elements to 1,758; touch targets meet Android's 48dp rather than a smaller
platform's 44; "save this file" now actually produces a file, through the
system picker for documents and straight into the gallery for images, where
before it silently did nothing at all.
The notification prompt is earned rather than spent. Android gives an app
two chances at it and then denies it for good, with no route back that anyone
finds. Concord was spending one of them four seconds into the first session,
on somebody who had just typed a passphrase and had never received a message.
It now waits for the first message you would have been told about, and asks
with a one-line bar pointing at something that just happened. The
notifications panel gained the row that makes deferring safe — the OS
permission itself, in the three states Android actually has.
History that proves who wrote it
Two lanes carry a guild's content and they authenticate different people.
Live gossip authenticates the sender, who is the author. History sync
authenticates whichever member answered the catch-up — one person's copy of
their own disk, never re-checked against anybody it claims to come from. So
an ordina...
Concord v0.57.0
Concord v0.57.0
Almost nothing in this release is visible. It is about what your node does
when nobody is typing — how much of your data plan it spends, how much of
your battery it holds, and one bug that could divide a guild in half without
telling anyone. If you run Concord on a phone, this is the release to take.
Guilds sync in bytes, not megabytes
A catch-up request used to say only "here is where I got to", and the answer
was rebuilt from scratch every time: the guild's icon and banner, every
member's profile with their avatar and profile banner, every custom emoji,
the whole governance log. Members ask each other for that once a minute,
forever, in both directions. Measured between two peers holding identical
state, on one guild with ordinary art on it: 677 KB per idle sync. That is
40 MB an hour, per guild, per direction, to establish that nothing had
happened.
A request now carries a list of what you already hold, and the answer is the
difference. The same guild now settles an idle sync in 863 bytes.
Past a certain amount of decoration it was not syncing at all. The response
had no budget against the 1 MiB transport limit, so a guild with a large
icon, a banner, a custom emoji and one decorated member built an answer that
could not be written — the requester read an empty stream and the guild
never caught up. There was a point at which a guild was too decorated to
sync. The whole payload is now budgeted, in a priority order that answers
"what may a cosmetic image displace?" with "nothing": governance first, then
records, then messages, then art. Anything that does not fit comes in the
next round, and the requester asks straight away rather than waiting out the
minute.
Old and new peers interoperate in both directions; nothing here needed a
protocol version.
Your phone stops working for everyone else
A handset was running the same be-a-service-to-the-network stack as a desktop
on mains power, and paying for it out of a data plan.
- It no longer relays other people's traffic. Carriers hand every phone a
globally routable address, which was enough to start a relay service — so a
friend behind a restrictive network could route an entire evening's session
through a phone in somebody's pocket, with nothing metering it and nothing
in the UI saying so. - It no longer answers reachability probes for strangers, and no longer
builds the second network stack that service needs. - It keeps far fewer connections — 48 rather than the general-purpose
default of 192 — after making sure the things that must never be dropped
are exempt: guild members, your own linked devices, and the rendezvous
nodes your offline mail waits on. Two of those were not protected before. - It searches for peers less often when the search is finding nobody.
Discovery ran a full walk every fifteen seconds, all night, whether or not
it ever turned anyone up. It now backs off to four minutes when rounds stop
producing anyone new, and snaps back to eager the moment you join a guild,
return to the app, or regain the network. - It knows when you are on cellular. Android now reports whether the
connection is billed by the byte, and the discovery loops are held to a
one-minute floor when it is. Delivery is untouched: messages, mailbox
drains and sync run at the same speed on any network, and joining a guild
on cellular feels exactly like joining one on Wi-Fi. - Its gossip keeps a phone's pace — a two-second heartbeat instead of one
and a third of the idle chatter per topic, which for one member of one
twenty-channel guild is forty-two meshes that each wake half as often.
Desktops keep the tuned defaults, now written down rather than inherited. - Typing indicators cost nothing when no screen is on. Half a channel's
gossip is the typing topic, and nobody reads "Sara is typing…" from a
pocket. Backgrounding drops those meshes and keeps the message topics, so
messages keep arriving at full speed.
A Wi-Fi permission also comes off the install screen. It existed for local
network discovery that cannot run on Android at all, so it was asking for
something nobody was getting.
A minimised window sleeps like a pocketed phone
Background pacing has worked for a year and fired on exactly one platform.
Nothing but Android ever told the node the app was off screen, so a window
minimised to the tray on Friday afternoon and a browser tab left open behind
forty others kept the full foreground cadence all weekend.
Pages now report whether anyone is actually looking. Because one node can
have several windows and tabs attached at once, the answer is a vote across
all of them — any visible window keeps the node awake, so a hidden tab can
never put a window you are typing in to sleep. Becoming visible is reported
instantly, because you are right there waiting; becoming hidden waits a
moment, because alt-tabbing through four windows does not mean you stopped
looking.
A guild that split in two now finds its way back
This is the one thing here that was ever visible, and it was visible as the
app being broken in a way nobody could name.
Two people allowed to change who is in a guild can do it at the same moment,
from the same starting point, without having seen each other's change. From
then on there are two guilds wearing one name. Each half is perfectly
consistent on its own — everyone online, everyone a member, every message
readable to the people beside you — and not one word crosses, with
nothing anywhere saying why.
It was in fact detected, and then thrown away milliseconds later: a peer on
your own half of the split would answer readably and clear the alarm,
though a peer sharing your half can tell you nothing whatever about the half
you cannot see. The verdict is now kept per peer, so only the peer that gave
evidence can withdraw it. The stranded half also had no way to find out it
was stranded, and now learns it from the requests the other half is already
sending. Repair is aimed at the people standing on the branch you need to
reach, rather than at whoever answered first.
Driven on a four-member guild forced into the state: previously nine messages
over ninety seconds, none of them crossing, no device ever showing a warning.
Now delivery across a freshly forked guild resumes in about eight seconds.
"Can people reach me?" now measures instead of guessing
Every rung of that panel read your own settings and inferred, and the
inference was wrong in both directions. A home router hands out a routable
IPv6 address and drops every unsolicited packet to it, so those users were
told people could reach them. A home server behind a port-forward holds a
private address, so the machine most obviously reachable in the whole user
base was told to go and set up a rendezvous it did not need.
The panel now leads with evidence: somebody opened a connection straight to
this computer this session, it already happened, the route demonstrably
works. Where there is no such proof the old rungs remain, reworded honestly —
"your address says people can reach you directly", with a line saying nobody
actually has yet.
The same proof now gates volunteering as a relay. A desktop only carries
other people's traffic once an inbound connection has shown it can be
reached, so friends stop holding reservations on relays that cannot carry
anything — which was the thing stopping them looking for one that worked.
Saying what is playing, for 302 bytes
Announcing a track re-sent your entire profile — avatar, banner, bio,
colours — to every guild you are in, several times per song. Measured on an
ordinary decorated profile that is 102,822 bytes to report a song title.
It is now its own small message: 302 bytes. Dragging a scrubber no longer
broadcasts at all, and an idle guild no longer re-ships your pictures every
minute for as long as the music is on.
Your profile card
All twelve card frames are redrawn — the six that were still borders now
match the six that had become places. Each has one light and a temperature to
spend it against, real texture rather than flat gradients, a shadow cast onto
the card it surrounds, and none of them is symmetrical any more. Six of the
faults fixed this pass were things that rendered perfectly and were only
caught by looking at them.
The scenes reach the whole card again. Legibility used to be bought by laying
the card's background back over the bottom two thirds, which turned a
whole-card effect into a banner with a lid on it; names now carry their own
soft halo instead, which is indifferent to what is behind them and to what
colour you picked. The frame chips in settings show the frame around a
miniature card at the right proportions, so the overhang that is the entire
identity of a frame has somewhere to go — twelve frames used to come out as
twelve thin coloured outlines.
Six new theme packs, each built on a combination the existing forty-four left
empty, and you can turn the shine off: the specular sweep across nine of
the animated packs can be switched off wholesale, while the packs where that
light is the pack keep their motion and lose only the hot core. It defaults
on, because it is a matter of taste rather than a fault.
Also
- Push notification support is fully built and now asks the build whether it
can run, rather than a flag nobody set. It stays off until a Firebase
project is dropped in;docs/PUSH.mdwrites down exactly what that takes.
Delivery today is unchanged — live sockets, and mail collected on open. - The visibility vote could fill up with ghosts on the desktop build, at
which point a window you were looking straight at could not say so and the
node settled to its slow beat. A full vote now makes room by dropping a
client that reports nobody is looking.
*Android sideload APK: versionCode 5700. Desktop: Linux native, Windows
native plus installer, and web binaries for every...
Concord v0.56.0
Concord went public since the last release, and most of this is the work of
making it worth looking at: text that lays out correctly whatever script you
write in, a profile you can actually decorate, and a history you can carry.
Right-to-left text
Mixed Arabic, Hebrew, Persian or Urdu was being displayed reordered. Every
text surface was implicitly left-to-right, so the bidirectional algorithm
resolved a whole mixed paragraph against that, and the punctuation at the
boundary — the full stop ending a sentence, a comma between clauses — attached
to the wrong end. The text was stored correctly and drawn wrong, which reads as
though the sender typed it that way.
Composers and message bodies now take their direction line by line, so one
message can hold an English line and an Arabic line and lay both out correctly,
and the draft box is a real preview of the result. Code is held left-to-right,
because an Arabic comment would otherwise flip an entire snippet. Names carried
into fixed furniture are isolated so they cannot drag surrounding punctuation.
Arabic is also set in a typeface chosen for it. Every bundled family ships
Latin subsets only, so Arabic previously fell back to whatever the operating
system had — two unrelated typefaces in one sentence. Three companions are now
bundled, each scoped so that anyone who never types Arabic never downloads one.
Wear something
Sixty-one drawn decorations and rings, in one picker and one slot, painted
in any of twelve colourways or in your own colour. They are drawn rather
than pictured, so they take a colour and stay sharp at any size.
The rendering was rebuilt underneath. Decorations used to be flat fills with
four possible colours, which is why so many of them came out the same shade of
one thing. They now have real light — one direction for the whole library, a
shadow cast onto the face beneath, and a colour expanded into five shades in a
perceptual colour space, so picking red gives you red metal rather than a red
wash.
Your profile card
Twelve drawn animated scenes to play across it — a wraith crossing moonlit
woods, a lighthouse working a coastline, planets turning on their arms — and
the twenty-nine particle fields alongside them. Twelve frames to put around
the card. Forty-four theme packs behind all of it.
Your history
An encrypted backup of every guild and direct message that restores onto
another device, a full export in plain Markdown, and a guild can now agree
how long it keeps messages.
Also
A landing page and a guide to running your own rendezvous node. The profile
editor's live preview now shows the decoration you just picked. Dependencies
moved forward where they could: the DHT and pubsub bumps are in, and TURN is
held back deliberately — 4.1.4 moves to a transport version the rest of the
stack cannot compile against.
Concord v0.55.1
Concord v0.55.1
A security and correctness release. Everyone on v0.55.0 should update.
Security
- The local API now authenticates. The browser build served its whole
RPC surface on127.0.0.1:8787with no authentication, and that surface
includes the call that reveals your 24-word recovery phrase. Any program
running on your machine could have taken your identity. It now mints a
bearer token and hands it to the page through the URL it opens. - A former guild owner can no longer take a guild back. Governance
records are folded in sequence order, and the sequence number was chosen
by whoever signed the record. Someone who had handed a guild over could
sign a new record carrying an old number and have it replay from a time
when they still held authority. Backdated records arriving live are now
refused, and handing over ownership can no longer be blocked by a ban.
Fixes
- A node could wedge and go quiet. A lock-order inversion between the
relay service and libp2p's connection notifications could deadlock two
goroutines permanently. The visible symptom was the worst kind: the app
looked connected while receiving nothing, because the peer notifications
gossipsub depends on had stopped flowing. - Two data races on guild state, one of them on the path the whole UI reads.
- Direct messages stay in recency order as messages arrive, instead of
holding whatever order the last refresh produced. /gifand the other argless slash commands run on a single Enter.- Refreshing no longer flashes the login screen on the way back to the app.
- Logging in no longer replays every celebration still on screen.
New
- Can people reach me? in the Ctrl+K palette, and beside every invite
code. It reports whether friends can actually connect to you and offers
the one thing to do about it, rather than leaving discovery as an
exercise. The README now has a "Finding each other" table covering the
three situations you can be in and what each costs.
Android sideload APK: versionCode 5501. Desktop: Linux native, Windows
native plus installer, and web binaries for every OS. macOS native still
needs the manual workflow.
v0.8.3
Concord v0.8.3 — the WOW release: app-wide motion pass (springy modals, live voice equalizers, animated listening indicators, glassy command palette, aurora welcome), DMs sorted by recency, now-playing activity cards with album art + live progress, custom status never overridden. Plus everything from 0.8.1/0.8.2: QR device linking, signed Android APK.