Repository navigation
Expand file tree
/
Copy pathChanges.txt
More file actions
1252 lines (994 loc) · 52.9 KB
/
Copy pathChanges.txt
File metadata and controls
1252 lines (994 loc) · 52.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
Changes
=======
This file is organised in reverse-chronological order: newest releases first,
with the original SocialCalc daily development log (2007–2010) preserved
verbatim at the bottom.
EtherCalc bundles SocialCalc from the audreyt/socialcalc fork; see
../socialcalc/Changes.txt for SocialCalc library release notes (v3.0.x–3.1.x).
==============================================================================
Unreleased
==============================================================================
Security
- Private multi-sheet workbooks now initialize children with immutable
`meta:parent` delegation, closing a confidentiality exposure where
sub-sheets of a private room were anonymously readable. RoomDO derives
each child authorization verdict from the parent, rejects local-ACL
conflicts and occupied-room capture, and keeps workbook-internal
children out of the public rooms index. On 2026-08-11 a read-only D1
`rooms` index query plus anonymous probes of gate-exempt
`GET /_/<parent>/access` counted 1,883,069 rooms, 68,582 child-shaped
names (`<base>.<positive decimal>`), and 13,875 distinct candidate
parents — 13,873 public, 2 not-found, 0 private. Pre-fix children had
no access metadata, so `#mirrorIndex` treated them as public and wrote
them into the index; post-fix parented children are never indexed. No
remediation is required in hosted production: the fix is prospective and
closed the hole before anyone was exposed by it. The invokability facts
remain true and explain why a sweep was never needed (and would still
block anyone who later wants one): `/_do/backfill-children` and
`/_do/set-parent` exist only on the internal Durable Object dispatch
surface (`packages/worker/src/room.ts:371,374`) with no Worker route
proxying them; `ETHERCALC_MIGRATE_TOKEN` is unset in production so
operator-gated paths 404 (`packages/worker/src/lib/migrate-auth.ts:40`);
and `RoomDO.#postBackfillChildren` enforces `local.acl.owner === uid`
(`packages/worker/src/room.ts:916-928`), preventing operator sweeps of
user rooms. A hosted sweep would still require new code and a deploy (a
public Worker route, a provisioned token, and a policy choice between
owner-only vs. operator sweeps). The D1 count is a lower bound — a child
whose mirror write failed, or any self-hosted deployment, is not
represented — but zero is plausible rather than lucky: exposure required
a private room (passkeys reached production only around `d2afa90`,
2026-07-18) and multi-sheet use and a child that received content. New
children are safe on creation, and pristine pre-fix children are marked
on the next TOC write (`packages/worker/src/routes/multi-import.ts:415`).
- Multi-sheet append import maps a mixed-version RoomDO `501` on
`/_do/import-append-toc` to a retryable `503` with clear copy (Worker/DO
skew during deploy), instead of surfacing bare "Not implemented".
- Closed stored DOM-XSS and cross-origin `postMessage` seams in graph and
multi-sheet clients; HTML exports now use the same element, attribute, URL,
and CSS-fetch allowlist as the live editor.
- Bound native and legacy WebSocket mutations to the authenticated attachment
room, validate complete message schemas and resource caps, re-check auth on
every state-changing frame, and reap bounded Socket.IO handshake sessions.
- Added bounded auth/import bodies, ZIP/sheet/cell/command limits, AuthDO
challenge capacity and per-IP ceremony limits, revocable `__Host-ec_sess`
sessions, live-socket expiry, and fail-closed private-room authorization.
- Gated `/_timetrigger`, fixed partial scheduler retries, hardened trusted
proxy attribution and room-ID entropy, and added private-response no-store
plus CSP/HSTS/nosniff/referrer/permissions/COOP headers. CSP WebSocket
authority now comes from `ETHERCALC_ORIGIN`, not a spoofable `Host`.
- Hardened Docker/Helm defaults (read-only/no-new-privileges containers,
disabled service-account token mounting, fail-closed passkey anchors) and
upgraded docs image processing to `sharp` 0.35.3, resolving
GHSA-f88m-g3jw-g9cj in its bundled libvips.
- Made staging deploy validation pass `--config wrangler.toml` explicitly, so
Vite's generated production-config redirect cannot silently ignore
`--env staging`.
- Room URLs stay unlisted by design: every non-landing response now carries
`X-Robots-Tag: noindex, nofollow, noarchive`, and only `/` remains
indexable. `GET /robots.txt` returns a real robots file (Allow: `/` only)
instead of falling through the `/:room` catch-all as app HTML. The robots
file deliberately carries no `Disallow` directive — `Disallow` forbids
fetching, so a crawler blocked from a URL can never observe its `noindex`
header and already-indexed rooms would be stranded permanently.
- Multi-sheet workbook import (replace and append — xlsx/ods/fods and
csv/tsv/txt/socialcalc) is re-enabled for private rooms: children are
created with immutable `meta:parent` so they inherit the parent's access
instead of publishing as public. All formats share one guarded server
append door; the multi-sheet client no longer PUTs sub-rooms directly.
Tooling
- Migrated the repository interface to Vite+ 0.2.4: package operations use
`vp install/add/remove/update`, workspace tasks use `vp run`, local binaries
use `vp exec`, and package Vite/Vitest imports and scripts use Vite+.
- Replaced the Biome gate with Vite+ Oxlint while preserving the existing
three-rule lint contract and the repository's no-formatter-gate policy.
CI, deploy, nightly, LemmaScript, and npm validation jobs now use the pinned
`setup-vp` action and Vite+ commands.
- Pinned the underlying package manager as `bun@1.3.14`. Bun remains the
native runtime for Bun-specific tests/scripts and for Docker or packed-package
bootstrap/runtime paths where Vite+ is intentionally not shipped.
- Made root `vp build` and `vp dev` production-faithful full-stack workflows:
Vite+ prepares the curated browser assets once, then the Cloudflare Vite
plugin builds or serves the Worker, Durable Objects, bindings, and assets.
- Kept integrated multi-sheet development same-origin, made standalone port
8080 strict, and excluded generated Worker Vite output from npm tarballs.
- Hardened self-host Docker builds against local-state leakage: build context
excludes dependencies, Vite/Wrangler output, secrets, migration dumps, and
persistent room data; the smoke gate asserts the resulting image is clean.
- Fixed `scripts/ratchet-verify.sh` mutation score parsing in `jq` and `node`
branches by computing the score from mutant status counts rather than reading
a non-existent `mutationScore` property, resolving an unconditional parse
error (exit 2) during mutation ratchet audits.
Integration
- Prepared the headless adapter for the next SocialCalc release: bundle
transforms now fail loudly on upstream wrapper drift, and the DOM shim
supports safely serialized attributes, class lists, and text content used
by accessible sheet rendering.
- Added multi-sheet bridge regressions for exact TOC routing, child-room
isolation/mutation, and cross-sheet recalculation through sibling RoomDO
hydration. EtherCalc keeps canonical per-sheet persistence; the embedded
SocialCalc workbook remains the in-memory calculation and interchange model.
- Moved the account/access cluster into its in-flow fallback at or below the
measured 920px cutoff, avoiding overlap with SocialCalc's expanded menu while
preserving fixed top-right chrome on wider sheets.
Bug fixes
- Fixed `POST /_/:room` returning `202` on Durable Object command rejections,
which silently discarded writes. The route now propagates the DO status and
response body (such as `413 command exceeds sheet limits` when a batch exceeds
200,000 cells).
==============================================================================
0.20260717.0 — 2026-07-17
==============================================================================
Quality
- worker: restored the Stryker mutation-testing floor (break 90) with three
boundary-pinning tests — CSV formula neutralisation's numeric regex
(multi-digit integer/decimal/exponent branches, trigger-led non-numeric
values), rate-limit config parsing (`rps <= 0` boundary via a numerically-
zero-but-non-canonical `"0.0"` input), and RoomDO seed migration's batched
log/audit/chat key loop bounds (off-by-one). No source or config changes.
- SocialCalc 3.1.0 compatibility (client security model migration, dropped
regex-injected `sanitizeHTML` patch) shipped in 0.20260716.0 below; no
further SocialCalc changes in this release.
==============================================================================
0.20260716.0 — 2026-07-16
==============================================================================
Dependencies
- SocialCalc ^3.0.8 → ^3.1.0. SocialCalc 3.1.0 ships its own opt-in
rendering security model (`SocialCalc.Callbacks.untrustedContent`,
`securityPolicy.sanitizeHtml`, `EscapeUntrustedHtml`, `SafeUrlForRender`),
superseding EtherCalc's regex-injected `SocialCalc.sanitizeHTML` hook.
The client now enables `untrustedContent` at boot and wires DOMPurify
via `securityPolicy.sanitizeHtml`; the served `static/socialcalc.js` is
no longer regex-patched. `SocialCalc.sanitizeHTML` is still installed
for backward compatibility with cached pre-3.1.0 assets.
Packaging
- Root `test` script now uses `bun run --filter '*' --if-present test`
instead of `npm run --workspaces --if-present test` (Bun-native pattern).
- `package.json` `files[]` exclusions hardened: `!packages/e2e/` and
`!packages/oracle-harness/` directory-level excludes replace file-level
patterns that did not prevent leaks; `!**/stryker-setup-*.js` added for
Stryker temp files that leaked into published 0.20260710.1.
- Behavioral pack-manifest test (`scripts/build-assets.test.ts`) now
packs from the actual repo tree and asserts no e2e/oracle/stryker/test
artifacts leak into the published tarball.
- `scripts/install-runtime-deps.js` now hard-fails (exit 1) when Bun is
absent instead of silently succeeding with a warning.
Bug fixes
- XLSX imports now reject cells/merges beyond SocialCalc column `ZZ` with
explicit `400`/`ImportColumnOutOfRangeError`, instead of silently dropping
AAA+ payload during replay. Malformed merge end columns (`Infinity`,
negative, non-integer) fail closed without calling `encodeColumn`.
- Restored the multi-sheet interface as the default creation flow: the landing
page now opens `/=_new` (the legacy `/_new` single-sheet route remains
available). Sandstorm grain roots again classify the legacy `sheet` room:
migrated TOCs and fresh grains open the `/=sheet` workbook; if only a
rewrite-era `sheet1` exists, the root falls back to `/sheet1` so the upgrade
cannot hide user data.
Fixes #838.
- Browser clients now resolve relative `/_ws/:room` endpoints against the page
origin before constructing a WebSocket, restoring live synchronization in
Chrome 124 and other browsers that require absolute `ws://`/`wss://` URLs.
- API commands sent through `POST /_/:room` now broadcast to connected native
and Socket.IO clients after durable persistence, so open sheets update
immediately without a reload. Submit-form sibling writes now include their
room name for correct fan-out and index mirroring (#835).
Verification
- Promoted the Leanstral/LemmaScript pump into root `lemma/` (maintained
surface; spike kept as immutable Attempt 2 provenance):
- `lemmascript@0.5.13` root devDependency; `LemmaScript-files.txt` batch list
- scripts: `verify:dafny`, `verify:lean`, `verify:both`, `verify:context`,
`verify:request` (Bun-first; Dafny 16 VCs / 0 errors on `lemma/xlsx-a1.ts`)
- CI: `.github/workflows/lemmascript.yml` (Dafny check + Lean gen smoke;
no full `lake build`)
- Promoted clipboard-boundary Bun tests lock the SocialCalc 1-based
`lastcol` adapter challenged by Leanstral Attempt 2
- Historical Attempt 2 request/raw:
`spikes/leanstral-xlsx-coords/attempt-2-request.md`,
`spikes/leanstral-xlsx-coords/leanstral-raw.md`
Cleanup
- Removed obsolete pre-rewrite deployment artifacts: `.dotcloud/`, `.openshift/`,
`apache/`, root `nginx/` (legacy config dir — the active self-host proxy
recipe lives in `deploy/nginx/`), `misc/`, `dotcloud.js`, `dotcloud.yml`, and
`requirements.txt`.
- Removed dangling legacy Express/CoffeeScript entrypoints: `server.js`
(broken symlink), `main.js`, `sc.js`, `exports.js`, root `player.js`,
`player-broadcast.js`, and the empty root `_start` stub (unrelated to the
active `/_start` route, which the Worker still serves via `start.html`).
None were referenced by the current TypeScript/Cloudflare Worker build,
self-host image, docs, or tests.
==============================================================================
Unreleased
==============================================================================
Passkey (WebAuthn) accounts and private sheets — Phase A.
- The Worker is now its own WebAuthn relying party: `POST /_auth/
register-init|register-complete|login-init|login-complete` run the
ceremonies against a new singleton `AuthDO` (credentials keyed by
credential ID, usernameless discoverable login, replay-safe signature
counters, HMAC-signed ~30-day sessions). The session travels only in
an HttpOnly `ec_sess` cookie; `GET /_auth/whoami` and `POST
/_auth/logout` round out the surface. Zero third-party auth
dependencies beyond `@simplewebauthn/server`.
- Private sheets: `POST /_/private` creates an owner-only room and
`POST /_from/:template/private` copies a readable sheet into one,
both backed by a new atomic `POST /_do/init-private` primitive that
claims only truly-empty rooms (409 otherwise) and requires the
caller-owned ACL. RoomDO is the sole authorization boundary: every
`/_do/*` read/write, export, and WebSocket path enforces the
`meta:access`/`meta:acl` plane (deny-overrides; legacy `?auth=`
tokens are demoted on private rooms), the rooms index and D1 mirror
exclude private rooms, and full-storage replacement paths (snapshot
PUT, seed, install, delete) preserve the access plane — deletion
leaves a tombstone so private room names cannot be squatted, while
TTL expiry still reclaims them. Private rename/clone are refused
rather than declassified.
- Anonymous collaboration on public rooms is byte-identical; passkey
auth ships dark on self-host until `ETHERCALC_AUTH` plus the
`ETHERCALC_RP_ID`/`ETHERCALC_RP_NAME`/`ETHERCALC_ORIGIN` trust
anchors are configured (see docs/SELFHOST_HARDENING.md §7).
- Client: a dependency-free `static/passkey.js` adds Sign in / New
Private Sheet / Copy to Private / sign-out affordances plus a
private-sheet notice on both the landing and sheet pages.
Test tooling
- Added release-gated Playwright coverage for two-context native WebSocket
collaboration and reconnect; real CDP WebAuthn registration/login with
private RoomDO HTTP and WebSocket authorization; production Worker-served
multi-sheet assets; and a representative room CRUD/export lifecycle.
Viewer ACL coverage is not implied: no HTTP sharing route currently creates
a genuine read-only viewer state.
- Worker mutation testing (`packages/worker/stryker.conf.json`, `break: 90`):
Phase A's post-rebase-onto-main hardening pass (see
`docs/MUTATION_REPORT.md`'s 2026-07-17 addendum) closed test-gap
clusters in auth-do.ts, auth-session.ts/authorize.ts/ws-upgrade.ts, and
rate-limit.ts/room-create-limit.ts, restoring the combined score to
90.20% — at or above main's own pre-feature baseline (90.16%). No
source/config behavior changed; test-only.
Cleanup
- Removed obsolete pre-rewrite deployment artifacts: `.dotcloud/`, `.openshift/`,
`apache/`, root `nginx/` (legacy config dir — the active self-host proxy
recipe lives in `deploy/nginx/`), `misc/`, `dotcloud.js`, `dotcloud.yml`, and
`requirements.txt`.
- Removed dangling legacy Express/CoffeeScript entrypoints: `server.js`
(broken symlink), `main.js`, `sc.js`, `exports.js`, root `player.js`,
`player-broadcast.js`, and the empty root `_start` stub (unrelated to the
active `/_start` route, which the Worker still serves via `start.html`).
None were referenced by the current TypeScript/Cloudflare Worker build,
self-host image, docs, or tests.
==============================================================================
0.20260710.1 — 2026-07-10
==============================================================================
Bug-fix release.
- Fixed multi-sheet TOC persistence: `POST /_/:room` with `text/csv` was
executed as a raw command batch (a silent no-op), so the worker-served
multi-sheet UI (`/=:room`) never persisted its table of contents — sheets
vanished from the tab bar on reload. CSV bodies now convert through the
shared SheetJS import path into a `loadclipboard` command and reuse the
legacy paste enrichment: `paste A2 all` on an empty room, appending after
the last row otherwise, with `?row=N` still inserting a row first. The
202 response echoes the enriched command array so multi-sheet clients
(and HackFoldr) recover the persisted row number. Oversized CSV imports
return 413; a cell-less CSV is a 202 no-op. XLSX/ODS POST behavior is
unchanged. Covered by unit, Durable Object round-trip, pinned legacy
oracle, and Playwright lifecycle tests.
Test tooling
- Added a root `vite.config.ts` `test.projects` allowlist so `vp test`
(Vite+'s Vitest wrapper) aggregates each package's existing
`vitest.config.ts` / `vitest.node.config.ts` with its own environment,
pool, and coverage settings intact, instead of falling back to Vitest's
default whole-repo discovery (which mis-ran jsdom/workers-pool suites
under a bare `node` environment and tripped over the Playwright and
`bun:test` files that live outside Vitest). `bun test` / per-package
`bun run test` are unaffected.
==============================================================================
0.20260710.0 — 2026-07-10
==============================================================================
First public EtherCalc release since 0.20260612.4; folds in the previously
unpublished 0.20260612.5 and 0.20260619.0 candidates.
SocialCalc 3.0.8
- Bumped the browser and headless engine from socialcalc ^3.0.4 to ^3.0.8.
- Formula-reference rewrites now preserve quoted, absolute, sheet-qualified,
and named references across copy/fill/insert/delete/replace operations;
overflow beyond column ZZ becomes #REF!, and N:T-style range endpoints parse
correctly.
- Filldown/fillright preserve rectangular numeric series and interactive or
replayed ranges; structural-edit undo restores affected formulas and names.
- Formula evaluation now preserves error types through arithmetic, concat, IF,
and lookup operations; fixes comparison precedence, multi-character criteria,
NPV/IRR period handling, currency defaults, and merged-cell format copying.
- Upstream core sources moved to typechecked TypeScript while the distributed
runtime remains plain UMD JavaScript. EtherCalc's text-html sanitizer patch
now tolerates the 3.0.8 render-sink shape.
Workbook import
- Implemented single-sheet workbook import on PUT /_/:room and POST /_.
- Implemented multi-sheet workbook import on PUT /=:room.xlsx and
PUT /_/=:room/xlsx, fanned out to sub-rooms.
- Added multi-import integration tests, pure transform unit tests, and oracle
scenarios.
Client and build tooling
- Replaced the shell asset copier with a tested TypeScript builder while
preserving the curated assets/ contract and SocialCalc patches.
- Upgraded the client stack to Vite 8 and React 19, docs to Astro 7, and the
Worker stack to Wrangler 4.107 / Hono 4.12.
- Standardised Vitest/coverage on 4.1.9, upgraded Playwright to 1.61, moved the
repository to TypeScript 7.0.2, and added Biome as the PR lint gate.
Oracle replay / CI
- Relaxed XLSX/ODS ZIP comparison for equivalent SheetJS-vs-legacy layouts,
optional metadata entries, shared versus inline strings, and worksheet
metadata drift.
- Fixed ignored HTTP bodies, command-echo WebSocket matching, and room-index
replay configuration; retained 100% unit coverage on gated oracle sources.
- Ratcheted the oracle-harness mutation break threshold from 83 to 80 after
recomputing the survivor profile.
Packaging
- Made package.json's files allowlist authoritative for exclusions: npm packs
no longer include package tests, oracle/e2e suites, local .wrangler state,
mutation/coverage reports, caches, or generated dist source maps.
- Published packages rebuild the curated client assets before packing, ship
those assets plus the Bun lockfile, and install production workspace
dependencies for the packaged CLI without retaining dev/test trees.
Release verification
- `bun run lint` completed with only the known broken server.js symlink
warning; `bun run typecheck` and the full `bun run test` suite passed,
including 14 Playwright end-to-end scenarios.
- An isolated Cloudflare staging deployment used its own D1 database; 19 safe
HTTP routes matched production in status and content type, with only the
expected SocialCalc runtime body changing. A browser edit persisted through
the Worker and rehydrated after reload.
- Worker deploy dry-runs and a fresh npm pack/install smoke completed; the
installed CLI served the landing page and static assets with all required
Worker/SocialCalc workspace dependencies present and forbidden local/test
paths absent.
==============================================================================
0.20260612.4 — 2026-06-12
==============================================================================
Self-host hardening (SH-2/SH-3 completion)
- ETHERCALC_RATELIMIT optional per-IP HTTP token bucket (default off).
- ETHERCALC_ROOM_CREATE_LIMIT caps room minting per source (proxy compose
defaults on: 6/min).
- docker-compose.proxy.yml defaults ETHERCALC_EXPIRE=2592000 (30-day TTL).
Sandstorm (SH-6/SH-7)
- ETHERCALC_SANDSTORM=1 honours viewer vs editor via X-Sandstorm-Permissions.
- run_grain.sh disarms ETHERCALC_MIGRATE_TOKEN after first-load migration.
- sandstorm-pkgdef appVersion 202606124.
Helm
- config.rateLimit and config.roomCreateLimit env wiring.
==============================================================================
0.20260612.3 — 2026-06-12
==============================================================================
Security
- astro 6.4.6 + @astrojs/starlight 0.40.0 (docs): closes GHSA-j687-52p2-xcff,
GHSA-xr5h-phrj-8vxv.
- hono 4.12.25 (worker): multiple advisory fixes in the 4.12.x line.
Maintenance
- Oracle rooms-index fixtures corrected; docs canonical URL docs.ethercalc.net.
- GitHub Actions Cloudflare deploy auth (OAuth refresh + account id).
==============================================================================
0.20260612.2 — 2026-06-12
==============================================================================
Dependencies
- socialcalc 3.0.3 on npm, then ^3.0.4 (quote-escaping, https text-links,
page-up/down visible-row span). Closes #88, #358, #493, #501, #512.
Features
- Oracle harness: 27 scenarios (WS connect/ask-log/execute; XLSX/ODS exports).
- Starlight docs site; production deploy workflow (manual dispatch).
- client-multi: useTocPoll live TOC sync.
Fixes
- import: decode ArrayBuffer before CSV drag-drop detection (#435).
- client-multi: keep same-origin basePath in production builds; only redirect
localhost:8080 → :8000 in Vite dev. Fixes Sandstorm export/API #292.
- client: viewer export treats empty __MULTI__.rows as single-sheet mode.
Fixes #232.
Docs and tooling
- Migrate GitHub wiki to Starlight user guide (syntax, forms, tips, FAQ).
- Restore Sandstorm packaging on main (SANDSTORM.md, run_grain.sh, capnp).
- CONTRIBUTING.md triage runbook; docs build in CI; deploy-docs workflow.
- Triage scripts expanded; closed #25, #59, #262, #288, #335, #494, #789,
#800 with FAQ answers.
==============================================================================
0.20260612.1 — 2026-06-12
==============================================================================
Dependencies
- socialcalc 3.0.2 → 3.0.3: CSV formula import (#304), formatted export
(#355, #638), currency division (#577), filldown without editor.range2
(#314, #564, #769, #785).
Features
- POST /_do/clone + GET /:template/form 302 for form templates.
- Oracle harness: 22 scenarios (room CRUD, csv/html export, form redirect);
structural HTML matcher; nightly oracle-replay + staging dry-run.
- client-multi: stable tab keys (#635), rowsRev iframe sync (#698), Foldr
link dedupe (#727).
- E2E: fill-down persistence, multi rename, form clone specs.
Infrastructure
- Legacy LS/webpack tree removed from main; oracle docker pins pre-rewrite SHA.
- Triage batch-close of 29 verified-fixed issues.
==============================================================================
0.20260611.1 — 2026-06-11
==============================================================================
Self-host hardening
- ETHERCALC_DISABLE_ROOM_INDEX gates room discovery (default ON in Docker,
Helm, workerd); legacy ETHERCALC_CORS honoured as fallback.
- nginx proxy recipe (docker-compose.proxy.yml); keyless non-loopback warnings.
- Helm chart 0.3.1: restricted securityContext (non-root, RO rootfs).
- workerd null-binding fixes (unset env → null, not '').
Fixes
- Docker entrypoint chown keyed on /data/do — fixes anonymous-volume crash in
0.20260611.0. **Do not use the 0.20260611.0 image tag.**
==============================================================================
0.20260424.0 — 2026-04-24
==============================================================================
Migration
- bin/migrate-legacy.sh: turnkey Redis dump.rdb → workerd self-host in three
commands; migrate profile in docker-compose.yml; backup tarball + manifest.
==============================================================================
0.20260423.0 — 2026-04-23
==============================================================================
Export / import
- xlsx/ods/fods export walks sheet.cells — formulas, formats, merges,
comments preserved (#55, #234, #289, #305, #573, #717, #762, #783, #795).
- Multi-sheet export routes (GET /_/=:room/xlsx etc.) implemented.
- xlsx/ods import emits SocialCalc formula commands.
- Cross-sheet formulas via findCrossSheetRefs + /_do/snapshot sibling fetch.
==============================================================================
0.20260422.3 — 2026-04-22
==============================================================================
Self-host
- Docker image runs workerd serve directly (no wrangler metadata fetch hang).
- client export via fetch → blob download (popup-blocker + Sandstorm safe).
- CLI spawns bunx wrangler (no node/npm in container).
Quality
- Mutation ratchets across gated packages; README.mkdn → README.md for npm.
==============================================================================
0.20260422.0 — 2026-04-22
==============================================================================
Major release — TypeScript rewrite on npm
- Full Cloudflare stack (Hono + Durable Objects + D1 + KV + R2); Miniflare
locally; standalone workerd Docker self-host.
- Native WebSocket protocol + socket.io shim; React 18 client-multi.
- Redis → DO/D1 migration tool; oracle equivalence harness; 100% coverage CI.
- Republishes npm line after 0.20201228.1 (last LiveScript release).
==============================================================================
Legacy EtherCalc (Node + Redis + LiveScript, pre-rewrite)
==============================================================================
Last npm/Docker release before the TypeScript rewrite: 0.20201228.1.
Pin this tag (or docker-compose.legacy.yml) to stay on Redis-backed EtherCalc.
20191008.0 — Sandstorm version bump.
20190418.0 — livescript 1.6.0; multi-sheet tab-loss fix (#635, #640).
20160501.1 — Graph tab charting; F2 input toggle; Russian translation.
20160122.5 — Excel multi-spreadsheet export; row/column select-all.
20150822.1 — Initial Sandstorm App Market release.
Earlier npm tags (0.20120522.x through 0.20170704.x) tracked the Node/
Express + socket.io 0.9 era. See git tags for the full list.
==============================================================================
Sweet SocialCalc 0.x (2008)
==============================================================================
These release markers appear inline in the historical development log below.
0.1 — 2008-01-26
0.6 — 2008-02-07
0.6.1 — 2008-02-08
0.6.3 — 2008-02-15
==============================================================================
Historical Development Log (2007-12-27 through 2010-07-05)
==============================================================================
The following is the original SocialCalc daily development log, preserved
verbatim for historical reference.
2007-12-27:
Fixed missing 'nl' entry in typelookup plus entry. (=false+1 was failing)
Renamed scjstest to socialcalc-1.js.
Added load and save of sheetobj.names.
Added names to data sent by simpleedit to server.
2008-01-02:
Added ExecuteSheetCommand with "set sheet" commands.
Added GetStyleNum.
Added Parse class.
Added UndoStack class.
2008-01-03:
Added Undo/Redo and implemented it for set sheet commands.
2008-01-04:
Started set cell commands.
2008-01-07:
Split undo into PushChange, AddDo, and AddUndo.
Added CellFromStringParts and CellToString and "set coord all".
Added more set cell commands.
Added EditorApplySetCommandsToRange.
2008-01-08:
Fixed bug with clicking on cell below stuff not rendered yet.
2008-01-11:
Added more set commands
2008-01-14:
Moved to SocialCalcJSv2 hierarchy for revision control
Changed programs to use new hierarchy, e.g., image/ and data/
2008-01-15,16,17,18:
Created formula1.js to parse and execute formulas.
Added start of RecalcSheet to socialcalc-2.js.
2008-01-21:
Implemented function scaffolding.
Added range support to formulas.
Implemented series functions, including SUM, etc.
Fixed OLPC Fn key bug.
2008-01-23:
SocialCalc2Demo command/slist control.
Better keyboard focus handling when document window loses focus (passThru).
Added Home key to MoveECellWithKey.
Added multi-line to ExecuteSheetCommand, Parse class.
2008-01-24:
Built SocialCalc2Demo tabs: Plain, Source, Help, About
2008-01-26:
Added IF(condition,true-value,false-value) function.
Added erase All/Contents/Formats command.
Added filldown and fillright commands and SocialCalc.OffsetFormulaCoords.
Released Sweet SocialCalc 0.1.
2008-01-28:
Changed order of display on Show Source/doNewSheet for iPhone bug.
Added Math1 functions: ABS, ACOS, ASIN, ATAN, COS, DEGREES, EVEN, EXP, FACT, INT,
LN, LOG10, ODD, RADIANS, SIN, TAN
Added ZeroArgFunctions: FALSE(), NA(), NOW(), PI(), TODAY(), TRUE()
Added Math2Functions: ATAN2, MOD, POWER, TRUNC
2008-01-30:
Removed uses of "for (i in arrayobj)" for Prototype, etc., compatibility
2008-01-31:
Added clipboard to sheet, as well as to save/load sheet
Bumped save version to 1.4
Added copy, cut, paste, loadclipboard, clearclipboard commands
Added clipboard to Sweet SocialCalc source tab, with undo
2008-02-02:
Made clipboard global to SocialCalc object.
Updated simpleedit8.pl to have command list of socialcalc2demo1
2008-02-05:
Added merge and unmerge commands.
Added insertcol/row commands.
2008-02-06:
Added deletecol/row commands.
Added special values to formula parsing, like #REF!.
2008-02-07:
Added licensing information, breaking out socialcalctableeditor.js.
Released as version 0.6.
2008-02-08:
Changed URLs to be specific for About details and Attribution.
Bumped to 0.6.1
2008-02-09:
Removed substr(-numbers), which are not handled correctly by IE, in formula1.js, formatnumber2.js.
Fixed a bug with "+coord" - changed "sheetdata" to "sheet".
2008-02-10:
Made UI Sugar-like with more tabs, grayscale scrollbars.
2008-02-11:
Added ConvertSaveToOtherFormat and ConvertOtherFormatToSave to give multiple clipboard views
2008-02-13:
Added sort command.
2008-02-14:
Add primitive graph command.
2008-02-15:
Released 0.6.3.
2008-02-18:
Fixed sort "constant" bug which left a numeric constant (vtc) treated as text, not a number
Added SocialCalc.TestCriteria.
2008-02-19:
Added DAVERAGE, DCOUNT, DCOUNTA, DGET, DMAX, DMIN, DPRODUCT, DSTDEV, DSTDEVP, DSUM, DVAR, and DVARP.
Added HLOOKUP, VLOOKUP, MATCH, INDEX, COUNTIF, and SUMIF.
2008-02-21:
Added the rest of the wikiCalc/SocialCalc 1.1 normal functions.
2008-03-10:
Replaced cursor use of editor.ReplaceCell with editor.UpdateCellCSS for speedup.
2008-03-12:
Made 702 maximum number of columns (ZZ) for number to characters.
2008-03-14:
First implementation of socialcalcspeadsheetcontrol.js.
2008-03-20:
Fixed up name support in formulas, including getting names defined by formulas working.
2008-03-24:
Added SocialCalc.Formula.loadsheet for inter-sheet references.
2008-03-25:
Added comment value to cells.
Finished other places that were waiting for names or sheet references.
2008-03-27:
Added SaveEditorSettings and LoadEditorSettings.
2008-04-01:
Added tabs and views lists to SpreadsheetControl.
Tried rudimentary "audit" tab.
2008-04-03:
Finished audit tab and comment tab.
2008-04-04:
Changed formula parsing to allow "." inside alpha and made "_" alpha.
Added name commands to socialcalc-3.js.
Started and Names tab.
2008-04-05:
Finished Names tab.
2008-04-07:
Added SpreadsheetControlCreateSpreadsheetSave and DecodeSpreadsheetSave,
along with multipart-mime save format.
Fixed SocialCalc.SizeSSDiv: this.requestedHeight/Width => spreadsheet.requested...
2008-04-09:
Moved the Audit, Comment, and Names tab definition code into socialcalcspreadsheetcontrol.js.
Added linkstyle to RenderSheet, carried through RenderCell, passed to expand_markup.
Added text formats to list (plain, HTML, wiki, hidden), fixed bug with default text format.
Parameterized toolbarbackground and tabbackground.
2008-04-10:
Fixed bug introduced with linkstyle in rendering with linkstyle!=null.
Changed names of determine_value_type and format_value_for_display to camel case:
DetermineValueType and FormatValueForDisplay.
Added Clipboard tab and view to facilitate interim moving of data from Excel and web.
2008-04-13:
Fixed requestedHeight/Width not working in SpreadsheetControl.
2008-04-14:
Changed rowneedsrefresh in scroll up/down one row to be object instead of array for Prototype compatibility.
Added SocialCalc.SpreadsheetControlCreateCellHTML and SocialCalc.SpreadsheetControlCreateCellHTMLSave.
2008-04-16:
Fixed SocialCalc.Formula.OperandAsSheetName not handling coords and names to get sheet name.
2008-04-17:
Keyboard handing: Fixed initial "[" being ignored, backspace passing through to browser at top level (now treated as delete).
Fixed sheetname!range_name not working and numerous other sheetname! bugs.
Fixed handling of space after coord in formula.
If SocialCalc.Formula.SheetCache.loadsheet returns null, an error is displayed in addition to #REF!.
2008-04-18:
Added dorecalc argument to SocialCalc.ConvertSaveToOtherFormat.
2008-04-21:
Added editor.SettingsCallbacks.
Added named ranges to Sort.
2008-04-23:
Added SocialCalc.defaultvalues.minimumcolwidth.
Added column drag resizing.
Fixed bug with dragging cursor out of grid losing capture on Firefox and Safari.
2008-04-24:
Made drag resize display work in Opera.
Removed dependency on body setting for font-size:small.
Added more button bar buttons: Reverse, Wiki Text, Align, etc.
Added spreadsheet control ExportCallback.
2008-04-25:
Fixed thumbstatus font size.
More drag resize for Opera and Firefox 3.
2008-04-26:
Fixed merge button bug.
Added check to guard against drag resizing non-visible columns.
2008-04-30:
Created SocialCalc.Constants.
Fixed bug where circularreferencecell wasn't reset on new recalc.
2008-05-01:
Fixed bug where merged cells' grid hid borders to right and below.
Fixed horizontal control position off when not Verdana bug.
2008-05-02:
Added more SocialCalc.Constants.
Added optional IDs on table control elements.
Fixed double-click outside grid bug.
2008-05-04:
Added optional styles for table control elements.
Moved table control repeat settings to constants.
Made thumbstatus settings skinnable with constants.
2008-05-05:
More constants: The rest of socialcalctableeditor.js, formatnumber2.js, and some of formula1.js.
2008-05-06:
More constants: The rest of formula1.js and HighlightTypes for cursor and range.
Fixed recent bug with unable to click inside of input box.
Fixed -1 1/2 => -0.5 bug.
2008-05-09:
Mostly finished InputBox/InputEcho stuff.
Fixed SocialCalc.intFunc bug which led to 2/3/08 (all Feb) being wrong.
2008-05-11:
Fixes some "event" references that should have been "e" in socialcalctableeditor.js.
Made InputEcho draggable.
Added SocialCalc.ResizeTableEditor.
2008-05-12:
Finish up InputBox stuff, handling no change, clicking during partialexpr, etc.
Added editor.DisplayCellContents() to be used to keep formula bar up to date
Fixed some bugs with reporting errors in formulas detected during parsing
2008-05-13:
Fixed a focus outside the table editor bug with the input box.
Added ID to inputEcho.
2008-05-20:
Continued work on SettingsControls objects, including adding the ColorDropdown and CustomDropdown.
Fixed some bugs with sizing of the non-main views.
2008-05-21,22:
Completed Settings view display of existing settings.
Started GetValues and DecodeSheetSettings
2008-05-26:
Implemented BorderSides settings control
Connected DecodeCellSettings, etc., so Settings view works
Layouts now handle * in any value position (PrecomputeSheetFonts renamed to PrecomputeSheetFontsAndLayouts)
2008-05-28:
Added SocialCalc.EditorMouseInfo.ignore to turn off mousedown and dblclick detection temporarily.
Added defaultCommentClass, defaultCommentStyle, defaultCommentNoGridClass, and defaultCommentNoGridStyle
to Constants for SocialCalc.RenderCell and context.commentClassName,
context.commentClassCSS, etc., to RenderContext.
Created SocialCalc.ConstantsDefaultClasses and socialcalc.css.
2008-06-02:
Added buttons to socialcalcspreadsheetcontrol.js.
Reworked the default toolbars to use buttons with icon images added to images file.
Made Clipboard tab have multiple formats again.
Updated the Help tab text.
2008-06-04:
Fixed problem with * in font in EncodeCellAttributes
2008-06-07:
Made InputBox Firefox up/down arrow handling work in more cases, adding inputBox.skipOne
Fixed H/VLOOKUP functions ignoring the rangelookup value
2008-07-07:
Fixed blowup bug with "1 1/3" in SocialCalc.DetermineValueType
2008-07-08:
EditorRenderSheet now adds "fullgrid" as an element id. It's the element that gets sheet mouse clicks.
2008-07-09:
Fixed missing "1,234.5" format.
2008-07-17:
Added statusline callbacks.
Added more comments about SocialCalc.Cell.
Changed SocialCalc.CellFromStringParts to convert strings to numbers in vt, vtf, and vtc.
Fixed bug where vertical-align wasn't being enforced in SocialCalc.SetECellHeaders.
Redid keyboard handlers to follow http://unixpapa.com/js/key.html more,
fixing bugs with Safari 3.1 and Opera 9.5.
Changed SocialCalc.InputBoxOnClick to SocialCalc.InputBoxOnMouseDown (bug 796).
2008-07-18:
SocialCalc.DetermineValue now handles multiple commas (had missing "g" attribute) (bug 805).
2008-07-29:
SocialCalc.Formula.SheetCache upgraded to handle loading, status values, and recalc flags.
New recalc code:
Fixed: Remember to have UI check for circ references and display status
Fixed: =Self should be circ
Fixed: if calced value/type is the same, don't delete display value
2008-09-09:
Fixed formatnumber2.js ampmstr using non-existant constants
2008-09-10:
Moved CalculateColWidthData call into RenderSheet
Fixed bug with last two rows in pane not getting height calculations updated correctly in
CalculateEditorPositions
Removed SheetRenders before recalc
2008-09-11:
Fixed bug with pane slider into scrolled row/col too far: now it unscrolls second pane
(but must wait for position recalc, unfortunately)
2008-09-12:
Fixed bug that kept pan slider tracking lines from showing
2008-09-18:
EditorSaveEdit now does encodeForSave of text.
Added sheet.renderneeded and sheet.celldisplayneeded
Added SocialCalc.ScheduleSheetCommands and started using it instead of ExecuteCommands directly
2008-09-19:
ScheduleSheetCommands now starts a time-sliced loop to execute commands
2008-09-20:
2008-09-21:
2008-09-22:
2008-09-23:
Made commands, recalc, and display all be timer driven
Added deferred command execution
Added edit.busy
Made most of UI observe the busy flag, including Button and Drag additions
2008-11-06:
Added calcorder as a timesliced part of recalc.
2008-11-07:
Added recalc to coded and decoded sheet attributes.
Sheet recalc attribute is now followed, as well as new sheet.recalconce.
2008-11-10:
SpreadsheetControl now has a default status line.
Added recalc button to toolbar.
2008-11-11:
Recalc is now followed in mult-sheet recalc.
2008-11-14:
Tab switching is ignored away from sheet when editor.busy.
Initial spreadsheet.currentTab is now 0 after initialization.
Recalc button in toolbar is only displayed if recalc needed
2008-11-15:
Made spreadsheet.DoOnResize.
Fixed bug with ResizeTableEditor changing griddiv but not toplevel.
2008-11-16:
Made SocialCalc.ResetSheet only reset data stuff so Reload works in new event-driven world.
2008-11-19:
Sort range is now saved and loaded.
2008-11-24:
Added sheet.changedrendervalues to fix bugs with changed fonts, etc.
Added color chooser to settings tab
Made scroll wheel follow editor.busy
2008-11-26:
Updated simpleedit14.pl
Fixed some IE bugs with SocialCalc.SpreadsheetControlDecodeSpreadsheetSave with ^$
2008-11-29:
Fixed row hide setting load bug (typo).
2008-12-01:
Made simpleedit14.pl do server-side wikitext expansion differently than client-side.
Fixed bug with formula bar not being updated after commands (and at load).
Fixed bug with commands being executed during cell edit.
2008-12-04:
Made General format round to a maximum precision to lessen floating point artifacts.
2008-12-09:
Added editor.ctrlkeyFunction, including Ctrl-V handling.
Added embedded newline support to csv and tab format conversion.
2008-12-10:
Fixed bugs in simpleedit14.pl with loading unavailable sheets
2008-12-11:
Added undo maximum of 100.
2008-12-26:
Fixed SocialCalc.setStyles to allow colons in style values. (kratib reported)
Finally fixed FF 2 Mac losing Ctrl-V.
Changed grid line and pane divider color to something darker to better work on some laptops.
Fixed some bugs with sort settings not being preserved when updating column chooser info or loading
2008-12-28:
Fixed some bugs with displaying zero values related to infinity and NaN.
2009-01-14:
Fixed bug with reloading sheet with font settings.
2009-01-15:
First implementation of movepaste/moveinsert done.
Added swapcolors button to socialcalcspreadsheetcontrol.js.
2009-02-02:
Changed to SocialCalc.Callbacks.expand_wiki which takes subtypes.
Implemented demo samples of text-wiki-pagelink.