Live sessions are per document instead of one for the whole app.
- **Engine** (`crates/engine/src/live.rs`): `Session.live` is now `Lives`: the app-wide parts (the
installed link, the invite-link registration) plus one `Live` per session. One place resolves
"the session of this document": `Session::live()` (the active document's; status off when it
has none), `live_mut()`, `live_of(uid)`. Sync, undo, read-only checks, follow, cursor chat,
spotlight and presence all go through the session of the document they are about, so edits and
presence never cross. Sessions behind the active tab keep syncing; your presence there is empty.
- **Link** (`LiveLink`): every call takes the `SessionId` the engine picked and `poll` returns
`(SessionId, Event)`, so one link carries several sessions. The desktop link and the browser
link keep them in a shared table with one mailbox per session
(`crates/collab-net/src/sessions.rs`); each hosted document listens on its own port.
- **Commands**: `collab.*` act on the active document's session; no parameters changed.
`collab.peers` keeps its shape and gains `session` (its id, null when the document isn't in
one) and `sessions` (every session: `{session, document, status, reason, role, fileName, link,
peers}`). `collab.join` returns the new session and opens its file in a tab of its own without
touching other sessions; until then (or if it fails) it shows on the tab it was started from.
Starting a session for a document replaces only that document's own session. Closing a document
ends its session only.
- **UI** (`crates/ui-egui/src/live.rs`): the Share popover, avatars, cursors, chat, spotlight
banner, follow frame and read-only reason read the session of the tab in front, per window. What
belongs to one session in the window (cursors shown, your chat box, a report being written) is
dropped when another tab comes to the front. `ui.inspect`'s `live` gains `session` and
`sessions`. No new strings or command ids.
- **Docs**: `docs/collab-protocol.md`, `docs/mcp.md`, `docs/control-protocol.md`,
`docs/collaboration-parity.md`, `docs/gaps.md` (gap 2), `ROADMAP.md`.
Third review:
- a broadcast counts once per recipient block, welcomes don't count
against block caps, and presence, cursor chat and pings have their own
per-address budget, so an office behind one address works together
- when everyone's reading budget runs out, the biggest readers are cut
off until it fits (their bytes freed at once), no cliff
- block counters are forgotten once only the table holds them
- install.sh: root's downloads, clone, swap file and built binary in a
root-only directory; the build user builds its own copy; the binary is
copied out with the build user's rights
- recommended open-mode settings in install.sh's config and the docs
Fixes from the security review of the public server:
- team libraries only in key-published files on open servers; unpacking
bounded by the file cap (zip bombs refused)
- byte-bounded send queues per connection and in all, a bound on bytes
waiting for the hub, per-address and global download budgets
- batches checked against the file, per-address and total caps as files
grow (refused with a Reject the app rolls back); the core's recent-batch
memory bounded in bytes; nothing confirmed when a log can't be written
- usage reports kept in memory; per-kind message caps checked while read
- layered IPv6 limits (/64, /56, /48), a global publish rate, connections
reserved for sessions, short idle timeout for one-shot requests
- revoked links and removed files cut library connections
- data directory 0700, server key 0600 from creation
- install.sh: SSH ports from sshd, ufw left off when other services
listen, unprivileged builds of the latest tag, settings checked before
use, own Caddy snippet; systemd memory and network caps; backups check
free space and say they hold the key
- linger reads raw bytes after an oversized frame (tungstenite asserted)
A file's library can be published to figcraft-server and enabled in other files by a share link (view access is enough); placing, styles, variables and library updates with overrides kept work as for local libraries, with a background check while the Assets panel is open. library.swap re-points instances, styles and variables to another library by name (one undo step, unmatched items listed). The server keeps per-file usage reports (library.usage).
figcraft-server hosts many files over the same hub the app uses for sessions, persists each as a checksummed snapshot plus an append-only log (synced before replies, compacted, damaged files skipped and reported) so restarts lose nothing confirmed, and gives per-file share links with edit or view access (hashed 128-bit tokens, revocable). The app can publish a file to a server and join server-hosted files; view-only tabs refuse edits. Protocol version 2.