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.
| Name | Type | Description |
|---|---|---|
| Text style | toolbar / ⌘B ⌘I ⌘U | Bold, italic, underline, strikethrough. |
| Colours | toolbar | Text and fill colour from a fixed palette, or cleared back to the theme default. |
| Alignment | toolbar | Left, centre, right. Clicking the active one returns the cell to automatic: numbers right, text left. |
| Number format | toolbar | Automatic, number, integer, currency, percent, date. |
| Column width / row height | drag the header edge | Stored in the workbook, so it survives a reload and other people see it. |
Permissions#
Three levels, granted per member under Team → Sheets.
| Name | Type | Description |
|---|---|---|
| sheets.view | read | Open workbooks and read them. Cannot type, format or resize. |
| sheets.edit | write | Everything in view, plus editing cells, formatting and resizing. Implies sheets.view. |
| sheets.manage | admin | Everything 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_…).
/workbooks/workbooks/workbooks/{id}/workbooks/{id}/workbooks/{id}/workbooks/{id}/ops/workbooks/{id}/stream/workbooks/{id}/presence/workbooks/{id}/compactGET /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.
| Name | Type | Description |
|---|---|---|
| Visible grid | 1000 × 50 | Rows × columns shown in the interface. The engine's own address space is larger; the window is a rendering choice. |
| Single change | 512 KB | Rejected above this. Reached only by formatting very large ranges — see the formatting cap. |
| Formatting per action | 20.000 cells | Client-side cap, set below the 512 KB server limit on purpose. |
| Presence timeout | 30 s | A participant disappears from the bar 30 seconds after their last heartbeat. |
| Stream lifetime | 10 min | Then the client reconnects. Idle proxies are the reason it is not indefinite. |
| Offline edits | in-memory only | Failed saves are retried on the next write, but they are not stored on disk. Closing the tab loses them. |