EtherCalc: a self-hosted collaborative web spreadsheet
Node.js port of Multi-user SocialCalc
At a glance
- What is it?
- EtherCalc is a web spreadsheet for real-time collaborative editing, rewritten in TypeScript on the Cloudflare fullstack. It self-hosts with docker compose up and keeps anonymous read/write for anyone who knows a room URL, which decides how you have to deploy it.
- Who is it for?
- Adopt EtherCalc when you want a spreadsheet that several people edit at once and you are willing to run it yourself: docker compose up -d on a trusted network, or the proxy compose file with TLS and rate limits if the instance is reachable from the internet. Skip it if you need per-cell access control out of the box, since anonymous read and write is the default and ETHERCALC_KEY only adds a per-room HMAC for edit and delete URLs.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 16 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What EtherCalc does that a shared file cannot
A spreadsheet sent as a file has one writer at a time. EtherCalc is a web spreadsheet for real-time collaborative editing: several browsers open the same room and see each other's edits as they happen. The README points at docs.ethercalc.net for the user guide and at API.md for the REST surface, and the project has been integrated into content management systems, with Drupal's sheetnode listed as an example.
The audience is narrower than "anyone who needs a spreadsheet". It is people who want a spreadsheet URL they can hand to a group, on infrastructure they control. The README states that the Docker path needs no Cloudflare account and no Redis, which matters if the reason you are looking at this project is that you do not want a hosted service holding the data. Browsers tested are Safari, Chrome and Firefox, so the client side is aimed at ordinary browsers rather than a native app.
Rooms, Durable Objects and where the bytes live
The current branch is a TypeScript rewrite on the Cloudflare fullstack: Hono, Workers, Durable Objects, D1, KV and R2. In the Docker image, the compose file comments say room state lives in Durable Object SQLite files under the ./ethercalc-data directory bind-mounted at /data in the container, and that no Redis is involved. That is the whole persistence story for a self-hosted instance: one writable mount, and everything else in the container is read-only.
The Dockerfile explains why the image runs workerd directly instead of wrangler dev. Wrangler's startup fetches Cloudflare metadata, and the comment says that fails in CI runners and in network-sandboxed environments such as Sandstorm grains, with an "Unexpected server response: 101" that stops the worker from binding a port. The build pipeline inside the image installs workspace deps, builds the static tree, and produces a bundled ES module that workerd serves. So the container is a standalone Worker with no control-plane dependency, which is the design choice that makes the no-Cloudflare-account claim true.
The CLI path is different: npm install -g ethercalc requires Bun 1.1 or later on PATH because the CLI spawns bunx wrangler, and it boots wrangler plus Miniflare. Both paths need no Cloudflare account, but they are not the same runtime, and the Docker path is the one the README recommends for self-hosting.
Install with Docker and open a first room
The README gives a three-command install for local or trusted-LAN use. Clone the repository, enter it, and start the compose stack. The service binds 127.0.0.1:8000 by default in docker-compose.yml, so it is local-only until you change that.
git clone https://github.com/audreyt/ethercalc
cd ethercalc
docker compose up -dAfter that, the app is at http://localhost:8000 and room state is persisted to ./ethercalc-data/ in the repository. The README is explicit that this path is for a trusted network only: localhost, an office LAN, or a VPN. It binds plaintext HTTP with no rate limiting and no TLS.
If you prefer npm, the CLI needs Bun 1.1 or later on PATH. The README gives this sequence, and the process starts on port 8000.
npm install -g ethercalc
ethercalcThe CLI accepts the legacy flag surface and forwards to wrangler dev. The README documents the flags as --key, --cors, --port, --host, --expire, --basepath and --persist-to, and says to run bin/ethercalc --help for the full table. One caveat is stated directly: --keyfile and --certfile are accepted for backward compatibility but currently print a warning, because wrangler dev does not expose TLS. Terminate TLS at a reverse proxy instead.
For an internet-facing instance the README requires the proxy compose file rather than the default one. It boots the same app behind nginx with the configuration at deploy/nginx/ethercalc.conf, which sets a 25 MiB body limit to match the Worker write cap, applies request and connection rate limits per source address, and forwards WebSocket upgrades with long read timeouts so idle spreadsheets stay connected.
docker compose -f docker-compose.proxy.yml up -dFor production HTTPS, place certificates under deploy/nginx/certs/, uncomment the 443 listener in that file, and uncomment the 443 ports mapping in docker-compose.proxy.yml. The README also warns against combining the bundled proxy with ETHERCALC_BASEPATH, because the proxy config does no prefix stripping.
The anonymous room URL is the security model
This is the part to read twice. The README says the app deliberately keeps anonymous read and write for anyone who knows a room URL, and that the edge is where you bound request volume. There is no per-cell permission system in the default deployment. If the URL leaks, the contents are readable and writable by whoever holds it.
The environment table offers partial answers, and it is worth being precise about how partial. ETHERCALC_KEY is an HMAC secret that enables read-only versus edit authentication, so edit and delete URLs require a per-room HMAC rather than being anonymous. That is not the same as accounts. ETHERCALC_AUTH=1 turns on passkey accounts and private rooms, but it requires ETHERCALC_RP_ID, ETHERCALC_RP_NAME and ETHERCALC_ORIGIN to be set together, and the RP ID and origin must match the HTTPS site users visit. So the strong option exists, but it is a WebAuthn configuration you have to get right, not a checkbox.
The room index is the other leak. ETHERCALC_DISABLE_ROOM_INDEX defaults to 1, hiding /_rooms* and /_exists/:room. The README notes that setting it to 0 reopens them, and that on the Docker image the directory endpoints then return empty bodies because there is no D1 index, so only /_exists becomes a live oracle: a way for a stranger to test whether a given room name exists. Leave the default alone unless you actually want a public directory.
ETHERCALC_RATELIMIT is described as an optional in-Worker per-IP limit that is off by default, with 1 or 10 meaning 10 requests per second and 60:600 meaning 600 per minute. The README calls it belt-and-suspenders behind nginx and explicitly not a substitute for the proxy. ETHERCALC_ROOM_CREATE_LIMIT caps room creation per IP across POST /_, /_new, /_from and PUT /_/room, with 1 meaning 6 per minute; the proxy compose defaults it on. ETHERCALC_EXPIRE prunes a room after a number of seconds of inactivity, and the README suggests 2592000 for a 30-day TTL on a public scratch instance.
Where EtherCalc is the wrong tool
If you need a spreadsheet where different people have different rights inside the same document, EtherCalc is the wrong starting point. The default is anonymous read and write per room URL, and the two stronger modes are a per-room HMAC and WebAuthn passkeys, neither of which gives you row-level or column-level permissions. A finance team sharing one workbook with restricted tabs should look elsewhere.
There is also an operational failure mode that has nothing to do with the app logic. The README documents an intermittent quirk on Apple Silicon where Docker Desktop's virtio networking can make curl localhost:8000 hang even against a healthy container. The suggested workarounds are to run the app directly with vp dev, or to use a Linux host. If your developers are on Apple Silicon laptops and you expect them to run this locally, budget for that.
Finally, the legacy migration path is not a normal upgrade. The compose file describes a migration profile activated with --profile migrate that adds two short-lived services, legacy-redis and migrator, to ingest a legacy Redis dump.rdb into the new Worker in one shot, driven by bin/migrate-legacy.sh. The file states that end users should not invoke the profile directly. If you are carrying an old Redis-backed EtherCalc, that script is the route, and it is a one-shot ingest rather than a rolling migration.
Alternatives and how the approach differs
The obvious comparison is a hosted collaborative spreadsheet. The difference is not features, it is where the data and the account model live. A hosted service gives you identity, sharing controls and revision history as part of the product. EtherCalc gives you a room URL and a container you run. If your reason for choosing EtherCalc is data residency or an offline network, the hosted option fails on the requirement itself, and the anonymous-by-default model is the price of not having an account system in the middle.
A second comparison is a wiki or CMS plugin that embeds a grid. The repository lists Drupal's sheetnode as an integration, which is the opposite arrangement: the CMS owns identity and permissions, and the spreadsheet is a component inside it. That is often the better fit when you already have a CMS with logins, because you inherit its access control instead of rebuilding it with ETHERCALC_AUTH and the three trust anchors.
A third option is running the older Node and Redis EtherCalc rather than this TypeScript rewrite. The repository still ships docker-compose.legacy.yml, and the migration profile exists precisely because instances in that shape are out there. The trade is that the rewrite removes Redis and runs a standalone workerd Worker with a single writable mount, while the legacy stack is the one your existing runbooks and backups were probably written for.
Licence, maintenance and what an upgrade costs
The repository's LICENSE.txt is present, but the hosting metadata reports the licence as NOASSERTION, while package.json declares CC0-1.0. Those two signals disagree, and CC0 is a public-domain dedication rather than a permissive software licence with attribution terms. If you are adopting this inside a company, read LICENSE.txt itself and have someone who can speak to licence policy confirm which terms apply to the files you ship. This is a description of what the repository says, not legal advice.
The last push to the default branch was on 2026-09-15, and the most recent release is 0.20260717.0 from 2026-07-17. The repository is not archived. The version scheme is date-based, which means releases are cheap to cut and the number tells you the date rather than a semantic compatibility promise.
Upgrade cost is concentrated in two places. Configuration is environment variables, and the README's recommended public-instance settings are a list you should re-check on each upgrade: ETHERCALC_KEY for HMAC-protected edit and delete URLs, ETHERCALC_DISABLE_ROOM_INDEX left at 1, ETHERCALC_EXPIRE for public scratch instances, and the proxy compose file for anything internet-facing. The second place is the proxy config at deploy/nginx/ethercalc.conf: if you copied its limits into your own nginx, caddy or traefik edge, an upstream change to that file will not reach you. The README's own instruction is to copy the same limits to your existing edge, which is exactly the arrangement that drifts.
Editorial conclusion
Adopt EtherCalc when you want a spreadsheet that several people edit at once and you are willing to run it yourself: docker compose up -d on a trusted network, or the proxy compose file with TLS and rate limits if the instance is reachable from the internet. Skip it if you need per-cell access control out of the box, since anonymous read and write is the default and ETHERCALC_KEY only adds a per-room HMAC for edit and delete URLs. Before rolling it out, verify that ETHERCALC_DISABLE_ROOM_INDEX is still 1, that ETHERCALC_EXPIRE is set for a public scratch instance, and that your proxy body limit matches the 25 MiB cap in deploy/nginx/ethercalc.conf.
Frequently asked questions
Is EtherCalc safe to expose to the internet?
Not with plain docker compose up. The README states that the app deliberately keeps anonymous read and write for anyone who knows a room URL, and that an internet-facing instance must sit behind a reverse proxy that terminates TLS and applies rate limits. The repository ships docker-compose.proxy.yml for that purpose.
What is EtherCalc, in simple words?
The README describes it as a web spreadsheet for real-time collaborative editing, and this branch is the TypeScript rewrite on the Cloudflare fullstack with Hono, Workers, Durable Objects, D1, KV and R2. Several people open the same room URL and edit the same sheet at once.
What is the most popular spreadsheet software?
The README does not compare EtherCalc with other spreadsheet products or rank them by popularity. It documents EtherCalc's own install paths, environment variables and API reference in API.md, and lists Drupal's sheetnode as a content management integration.
Why is MS Excel called an electronic spreadsheet?
The README does not discuss Excel or the history of the term electronic spreadsheet. It covers EtherCalc's own architecture, the docker compose install, the ETHERCALC_* environment variables and the REST API in API.md.
Who invented spreadsheets?
The README does not cover the history of spreadsheets or their inventors. EtherCalc itself is a Node.js port of Multi-user SocialCalc, and this branch is a TypeScript rewrite deployed to Cloudflare via wrangler deploy or self-hosted with docker compose up.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/audreyt-ethercalc)