Reference / Sheets

Sheets

Spreadsheets that several people can edit at the same time, with a real formula engine, cell formatting, and cursors you can see. This page explains how it works underneath — because the architecture decides what you can and cannot ask of it.

How a workbook is stored#

One decision shapes everything else on this page, so it comes first.

A workbook is not a table of cells in the database. It is a snapshot— the engine's own binary representation of the whole workbook — plus an ordered log of changes applied on top of it. Opening a workbook means loading the snapshot and replaying the log; saving means appending to the log. Periodically the client writes a fresh snapshot and the log behind it is pruned.

This was measured, not assumed. Rebuilding a 20.000-cell workbook from per-cell database rows took 27 seconds. Rebuilding it from the engine's snapshot took 1–3 milliseconds. Keeping cells individually queryable would have cost the app its ability to open.

What that costs you#

Cell contents cannot be queried on the server.There is no endpoint that answers "which workbooks mention this invoice number", and server-side export does not exist. Search and export happen in the browser, on the workbook you have open.

What it does notcost you is ownership: the data is in Sentroy's own database, so backups, company deletion and the KVKK chain all work exactly as they do for Mail and Storage. Deleting a company deletes its workbooks, their snapshots, their change logs and their presence records.

Editing together#

Why two people typing in the same cell do not end up with two different spreadsheets.

Every client runs its own copy of the formula engine and sends its changes as diffs. Those diffs are nota CRDT — this was tested: two clients that write the same cell and then apply each other's diff end up with different workbooks. Convergence comes from somewhere else.

The server assigns every change a sequence number, and every client applies changes in exactly that order. A write says which sequence it was based on; if the server has newer changes, it returns them in the same response, so the writer catches up as a side effect of writing. Live updates arrive over a stream on top of that — but the stream is an accelerator, not the source of truth. If it drops an event, the next write repairs it.

Presence#

While a workbook is open, each participant heartbeats their cursor every ten seconds; a record expires thirty seconds after the last beat. The bar at the top shows everyone else — you are filtered out on the server, because you already know where you are. A grey dot means the live connection dropped; you can keep typing, and your changes are held and retried, but others will not see them until it returns.

Formulas#

A real dependency graph, not a string evaluator.

Formulas are evaluated by IronCalc (MIT/Apache-2.0), compiled to WebAssembly and loaded lazily — it is not part of the app shell, and a workbook you never open never downloads it. It keeps a dependency graph, so changing A1 recalculates only what depends on A1, and circular references are detected rather than hanging.

Because the engine runs in your browser, formulas are never recalculated on the server. Nothing recomputes while a workbook is closed: no scheduled refresh, no server-side NOW().

Formatting#

Bold, colours, alignment and number formats — and one limit worth knowing.

NameTypeDescription
Text styletoolbar / ⌘B ⌘I ⌘UBold, italic, underline, strikethrough.
ColourstoolbarText and fill colour from a fixed palette, or cleared back to the theme default.
AlignmenttoolbarLeft, centre, right. Clicking the active one returns the cell to automatic: numbers right, text left.
Number formattoolbarAutomatic, number, integer, currency, percent, date.
Column width / row heightdrag the header edgeStored in the workbook, so it survives a reload and other people see it.

Permissions#

Three levels, granted per member under Team → Sheets.

NameTypeDescription
sheets.viewreadOpen workbooks and read them. Cannot type, format or resize.
sheets.editwriteEverything in view, plus editing cells, formatting and resizing. Implies sheets.view.
sheets.manageadminEverything in edit, plus creating, renaming and deleting workbooks. Implies sheets.edit.

Company owners and admins have all three implicitly; a plain member has only what is ticked. The implications above are enforced on the server, so granting sheets.manage alone is enough — you do not need to tick all three.

Endpoints#

Listed for completeness and for debugging. The change payloads are engine-generated; see the warning at the top of this page.

All paths are relative to /api/companies/{slug} on https://sheets.sentroy.com, and accept either a dashboard session or a Bearer access token (stk_…).

GET/workbooks
POST/workbooks
GET/workbooks/{id}
PATCH/workbooks/{id}
DELETE/workbooks/{id}
POST/workbooks/{id}/ops
GET/workbooks/{id}/stream
POST/workbooks/{id}/presence
POST/workbooks/{id}/compact

GET /workbooks/{id} returns the snapshot, the changes recorded after it, and the sequence number they add up to. /ops appends changes and returns the ones you had not seen. /stream is a Server-Sent Events channel that pushes new changes and presence; it closes itself after ten minutes and the client reconnects.

Limits#

The numbers you would otherwise have to discover by hitting them.

NameTypeDescription
Visible grid1000 × 50Rows × columns shown in the interface. The engine's own address space is larger; the window is a rendering choice.
Single change512 KBRejected above this. Reached only by formatting very large ranges — see the formatting cap.
Formatting per action20.000 cellsClient-side cap, set below the 512 KB server limit on purpose.
Presence timeout30 sA participant disappears from the bar 30 seconds after their last heartbeat.
Stream lifetime10 minThen the client reconnects. Idle proxies are the reason it is not indefinite.
Offline editsin-memory onlyFailed saves are retried on the next write, but they are not stored on disk. Closing the tab loses them.