v0.9.0
Pre-release
Pre-release
The "public-launch readiness" release. The framework gets its own website (actor-ts.dev) with 199+ pages and full German translation; the cluster bootstrap shrinks from 15–30 lines to a single Cluster.bootstrap({ name }); eight latent security weaknesses get patched; and a code-quality sprint closes 17 audit-catalog issues.
🚀 New features
Persistence
eventDispatcher<S, E>()— typed builder forPersistentActor.onEventthat the compiler refuses to complete until every variant of the event union has a handler. Missing variants surface asEventDispatcherIncomplete<missing>type errors at the build site (#239).
Chat sample feature sweep
- User-created rooms at runtime —
ChatRoomDirectoryActorwraps a cluster-wideDistributedDataORSet; protocol gainscreate-room/room-added/room-removed; six frontends grow a "+ new room" input (#98). - Private direct messages — DMs ride existing protocol frames as virtual
@<username>rooms; shardedDmChannelActorkeyed on the canonical pair-id; each user subscribes once to their inbox topic (#100). - Typing indicators — ephemeral
TypingBroadcastvia the room's PubSub topic; clients debounce at 1/2 s and auto-clear after 3 s (#103 slice 1). - Read receipts — per-room
read-up-to.<room>DistributedDataLWWMap;ReadReceiptsActorenforces a monotonic guard at the boundary; frontends render ✓ / ✓✓ on own messages (#103 slice 2). - Production-realistic auth — scrypt-hashed passwords (N=16384/r=8/p=1, constant-time verify); HMAC-SHA256-signed JWT-style session tokens self-validate without a DD read; revocation set in DD-LWWMap (#99).
⚠️ Security
Eight latent security weaknesses patched. All defenses are at the deserialisation / boundary layer with regression tests pinning both the attack vector and the legitimate path.
- Wire-frame size cap —
cluster/protocolrejects frames claiming gigabyte+ lengths before allocation; defeats a 4-GiB-claim memory-exhaustion DoS. Configurable;Infinitycap remains the escape hatch. - Path-traversal block in
FilesystemObjectStorageBackend— keys containing..or absolute-path patterns rejected at the boundary instead of being resolved through to disk. - Memcached protocol injection —
MemcachedCachekeys validated against the 250-byte / printable-ASCII rule before being placed on the wire; defeats injection via attacker-controlled keys. - Gossip-version cap against permanent-down exploit — versions more than 24 h above the local wall-clock are rejected on the spot; previously a malicious peer could send
version: MAX_SAFE_INTEGERto pin a healed node asdownforever. - Snapshot-seq validation on recovery —
PersistentActorrejects snapshots whoseseqNris non-monotonic with the journal; defeats tampered-snapshot replay. - WebSocket inbound frame size cap —
WebSocketActorrejects oversized inbound frames before assembly; defeats memory-exhaustion DoS via fragmented frames. - Duplicate-identity hello rejection —
cluster/transportrefuses a second hello frame claiming an already-connected identity; defeats peer-hijack where an attacker rebinds to a victim'sfromaddress. Legitimate reconnect (after clean close) unaffected. - Idempotency-key cache binding —
http/cache/idempotencyties each cached response to the request fingerprint (method + path + body hash) so a poisoned key can't replay one response across different requests.
✨ Quality of life
API shortcuts
Cluster.bootstrap({ name })— one-call setup that builds theActorSystem, joins the cluster, starts the Receptionist, and wiresSIGTERM/SIGINTshutdown. Discovery defaults to an env-driven chain (CLUSTER_SEEDS→ Kubernetes API → DNS) so the same code runs single-node in dev and joins an existing cluster in production without a config change.cluster.sharding.start('cart', CartActor, { extractEntityId })— getter onClusterplus class-shorthand onClusterSharding.start(); replaces the previousClusterSharding.get(system, cluster)+Props.create(() => new CartActor())ritual.ref.ask<TRes>(msg, timeoutMs?)— method form of the ask pattern with auto-injectedreplyTo. The freeask(ref, msg)function is removed (pre-1.0, no compat shim).system.spawnTyped(behavior, name)+system.spawnTypedAnonymous(behavior)— method form symmetric tospawn/spawnAnonymous; same pair lands onActorContextfor typed-child creation from untyped parents. The freespawnTyped()+spawnTypedChild()functions are removed.await system.http(8080).bind(routes)— Fastify-default HTTP shortcut.system.extension(HttpExtensionId).newServerAt(...)still works for non-default backends.ActorSystem.create('app', { persistence: { journal, snapshotStore } })— wire real persistence backends at creation time instead of poking the extension after the fact.
Code hygiene
- Pattern-match exhaustiveness pass — 9 discriminator-union dispatch sites converted from if/else-or-switch to
match(...).exhaustive(); adding a new union variant without a matching arm now fails the typecheck. TouchesBrokerActor.enqueueOutbound,JetStreamActor/MqttActor/KafkaActorcmd dispatch,BackoffSupervisor,HoconParser,Compression,BodyCodec,PersistentActor(#230, #232, #233, #234, #239, #240, #241, #243, #244). - DRY helpers —
src/util/Constants.tscentralises duplicated defaults (gossip interval, ask timeout, tombstone TTL, seed-retry);src/util/LazyImport.tsis the uniform peer-dep import + "missing package" error;src/util/WrapError.tsis the typed-error wrap helper with double-wrap prevention (#257, #252, #254). - Typed names —
src/config/ConfigKeys.tsis the typed const-tree for every HOCON path;src/persistence/storage/KeyValidator.tsis a declarative rule-based factory replacing hand-rolled key-safety checks (#265, #251). - Naming consistency — every message union now uses
kindas discriminator (not a mix oftype/op/cmd);ActorSystem.actorOfis gone in favour ofspawn/spawnAnonymous; the redundantactor-ts.prefix on worker message kinds is dropped.
📚 Documentation
- Public website at actor-ts.dev — Astro Starlight site under
docs/, 199+ pages across the 12-Part IA, full Quickstart + fundamentals + per-subsystem deep-dives + migration guides + API reference (TypeDoc). - Full German translation — every page mirrored under
/de/. Seven additional UI locales (fr, es, ja, ko, pt-BR, ru, zh-CN) staged with sidebar labels translated; full content translations tracked as open issues (#300–#306). - Mermaid diagrams throughout — replaces ASCII art across all subsystem pages (cluster, sharding, distributed-data, persistence, observability, operations, testing, IO, delivery).
- Landing-page polish — animated particle-network hero, prose-driven "What is actor-ts" cards, See-it-in-action status grid, custom-domain redirect, mobile-responsive splash.
- Issue templates + security disclosure flow —
.github/ISSUE_TEMPLATE/gainssecurity_report.yml; bug template gets a security-flag checkbox.