Files, presence & broadcast
const file = await db.$files.upload(blob, { path: "avatars/me.png" });const bytes = await db.$files.download("avatars/me.png");const all = await db.$files.list({ prefix: "avatars/" });await db.$files.delete("avatars/me.png");File bytes live in the FILES R2 bucket, with a 25 MB cap and member read / editor write. Their index is written through the transaction log, so a restore keeps it.
Presence
Section titled “Presence”Presence is ephemeral and rides the workspace socket:
const room = db.$presence("board:" + boardId);room.set({ name, color });room.cursor({ x, y });const stop = room.subscribe((peers) => render(peers));
// When leaving the room:room.leave();A presence room also exposes drag() and clearDrag() for drag state.
Broadcast
Section titled “Broadcast”Broadcast is a fire-and-forget message between the clients of a workspace, on the same socket:
const board = db.$broadcast<{ kind: "ping"; at: number }>("board:" + boardId);const stop = board.subscribe((message, from) => flash(from.userId, message));board.send({ kind: "ping", at: Date.now() }); // false (dropped) while offlineA refused topic subscription reports through the optional onError callback on subscribe. A message reaches every other client listening on the topic, with the sender’s client id and user id; your own messages are not echoed back. Nothing is stored or replayed, and a send while the socket is down is dropped rather than delivered late. Listening clients rejoin their topics after a reconnect.
By default any member of the workspace may listen and send. Decide per topic on the Worker:
import { defineTapeDB, WORKSPACE_ROLE_LEVELS } from "tapedb/worker";import schema from "./tapedb.schema";
defineTapeDB({ schema, topics: { authorize: ({ auth, topic, action }) => !topic.startsWith("admin:") || auth.role >= WORKSPACE_ROLE_LEVELS.ADMIN, },});React bindings: usePresence and useBroadcast in React.