Releases
Download the Wippy CLI for your platform.
Quick Install
curl -fsSL https://hub.wippy.ai/install.sh | shv0.3.46a
Runtime v0.3.46a includes 17 merged PRs since v0.3.45a. It improves process upgrade reliability, reduces inbox-delivery allocation churn, and fixes error handling, local module loading, and filesystem cleanup.
Reliable process upgrades and shutdown
- Unread messages survive Lua process upgrades (#937). The runtime transfers unread inbox messages, buffered listener deliveries, and retained messages to the initialized replacement process, preserving delivery order and message envelopes. Already-consumed messages are not replayed; incompatible mailbox handoffs fail explicitly.
- Terminal hangups shut down gracefully (#933). SIGHUP now follows the supervised shutdown path used by SIGINT and SIGTERM, giving services their normal cleanup opportunity.
Lower inbox-delivery churn
- Format inbox messages when delivery is possible (#938). The canonical inbox handler defers payload conversion until a receiving channel is ready, avoiding repeated conversion of stalled messages. Custom handlers and typed filters retain their eager evaluation behavior.
- Targeted regression benchmarks reduced a blocked retry from 4 allocations / 320 bytes to zero, and a 64-message inbox burst from 8,133 allocations / approximately 649 KB to 192 allocations / approximately 13.3 KB of cumulative allocated memory. These measurements describe the benchmark workloads, not application-wide retained memory.
Structured errors and lifecycle results
- Preserve array-shaped Lua error details (#921). go-lua v1.6.4 keeps nested arrays intact across returned, raised, and yielded errors. Its compiler and typechecker are unchanged from v1.6.2.
- Keep cancellation and termination error metadata (#936). Classified errors retain their category and explicit retryability. Cancelling an already-exited process reports NotFound through the normal error protocol.
Correct module loading and filesystem cleanup
- Bound local replacements to their module source root (#935). A replacement with a src directory loads declarations from there; flat-layout modules remain supported. Module-relative exclusions continue to apply, preventing unrelated repository declarations from entering dependency resolution.
- Remove directory symlinks safely (#922). Filesystem removal checks the named entry rather than following its target, so a symlink to a nonempty directory can be unlinked without deleting target contents. Real nonempty directories remain protected.
Host identity with policy enforcement
- Read the runtime process UID and GID (#923, #939). Lua gains system.process.uid() and system.process.gid(), returning the numeric OS user and group IDs. Each uses the existing system.read policy guard on its own uid or gid resource. Explicit denial returns PermissionDenied; unsupported platforms return Unavailable. Dedicated regressions cover policy denial for both calls.
Dependencies and builds
- Update Moby API to 1.56.1 and client to 0.6.1, OTel HTTP instrumentation to 0.72.0 and gRPC metric exporter to 1.47.0, SQLite driver to 1.51.0, Brotli to 1.2.6, S3 manager to 1.23.11, and optional Tailscale to 1.104.0.
- CI, nightly, and release builds select the Go toolchain from go.mod (Go 1.27.1).
- Release artifacts cover Linux amd64/arm64, macOS amd64/arm64, and Windows amd64, with SHA-256 checksums and detached Ed25519 signatures.
Known dependency risk
Excelize 2.11.0 has open upstream security advisories, including denial-of-service issues when parsing malformed spreadsheets. Applications processing untrusted workbooks should account for this remaining risk. GitHub tracks the advisories in this repository's dependency security alerts.
Verification
The exact release source passed all 19 main-branch CI jobs and both CodeQL analyses. Release automation builds and smoke-tests each native platform before signing and publishing its binary. The policy and scheduler-test follow-up also passed the actor and system-module race suites locally, including 1,000 repetitions of the corrected scheduler regression.
v0.3.45a
v0.3.45a focuses on lower allocation churn, faster startup, and more reliable registry, cluster, shutdown and terminal behavior. It includes 30 merged runtime PRs since v0.3.44a.
Compatibility and upgrade notes
- Native Go
.soextension loading and its manifest API have been removed (#897). Non-empty legacyextensionsconfiguration is rejected explicitly. Applications using this surface must migrate to statically supplied Go boot components or another supported execution surface. - Tailscale and Tree-sitter are now opt-in build features (#897). Applications using them need a custom build with
WIPPY_FEATURES=tailscale,WIPPY_FEATURES=treesitter, orWIPPY_FEATURES="tailscale treesitter". SQLite FTS5, vector search and preupdate-hook build flags remain enabled. - Existing ordinary process-registry lookups remain immediate. Waiting is explicit and EVENTUAL-only:
process.registry.lookup(name, process.registry.EVENTUAL, {timeout = "5s"})(#901).
Memory and execution churn
- Convert Go payloads to Lua in one traversal, eliminating intermediate map/slice copies. Preserve nested payloads, PIDs, time/duration values, structured errors, byte strings, struct tags and recipient isolation. Defer ordinary message materialization until a receiver or channel slot is available (#899).
- Retire replaced/deleted WASM function generations only after active calls drain and workers stop. Release failed creations and unused isolated preflight modules, preserving independently loaded guest state and limits (#902).
- Clear delivered mailbox slots, consumed resume/yield references and pooled message pointers. Reuse existing result ownership and avoid unnecessary package copies on ordinary unbounded traffic (#903, #905).
- Reduce channel/select allocation churn while preserving public result identity, ordering, scheduling and bounded-queue behavior (#904).
- Reduce bootstrap allocation hotspots in dependency lookup, wildcard alternatives, Lua import graphs, WASM instruction discovery and resource decompression (#902).
In matched credential-free Kickside startup benchmarks, #902 reduced settled-startup allocation by about 327–338 MiB and RSS by about 127–134 MiB. The two-channel actor benchmark fell from 35 to 20 allocations per message across #903/#904.
Startup, deployment and registry correctness
- Read authenticated embedded Lua cache entries in place and restore registries with less copying. Index the compressed seed and decompress entries only when used (#892, #912).
- Replace the deployment-owned dependency closure when updating a root application, rather than retaining obsolete owned pins. Preserve committed metadata and offline replay behavior (#889).
- Invalidate registry finder caches on published-state revisions, including overlays and rollbacks (#914).
- Add explicit timed EVENTUAL name waits, driven by registry changes rather than polling. Waits honor cancellation, shutdown and sibling isolation (#901).
Cluster, shutdown and upgrades
- Deliver held shard replies after a requester rejoins, bound retries and close the send/join race (#900).
- Release EVENTUAL names owned by a process when that process exits (#911).
- Deliver MonitorDown to processes monitoring a departed node; links retain their LinkDown behavior (#906).
- Leave cluster membership before closing internode transport (#908).
- Fail forwarded reads promptly when membership contains no Raft-eligible member, avoiding repeated delay on impossible forwards (#913).
- Make caller cancellation initiate graceful shutdown. Give OS signals one owner and distinguish programmatic shutdown requests from the second-signal force-exit path (#907).
- Carry process-owned lifecycle/context/terminal values through in-place upgrades and retain terminal creator authority after handle reattachment (#909, #910).
Security, native integration and diagnostics
- Acknowledge policy mutations only after the owning policy engine applies them, eliminating a race behind intermittent gateway refusals (#896).
- Label Docker containers at creation using host-selected identities, with validation and boundary tests (#894).
- Add optional
fs.directorylink_policy: owner_safefor safe external file reads on Unix. Containment remains the default; writes remain contained. Windows and other systems use the contained policy (#890). - Expose SQLite primary/extended result codes through structured Lua error details, including portable CGO/non-CGO handling (#891).
Standalone applications and terminals
- Add per-invocation-folder default state and an optional owned-state alternate command using the existing boot configuration and state lock. Select the alternate only on actual lock contention, keep its temporary data/configuration isolated, and preserve existing behavior when unset. Preserve root application identity when packing (#898).
- Replace the terminal stack with first-party
wippyai/tty. Fix the OSC UTF-8 title behavior for runtime consumers, including builder-produced binaries, and release saturated reply producers during shutdown (#915). - Remove the vendored ANSI fork and runtime
go.modreplacements. Import the SQLite fork by its canonical module path so downstream consumers build the tested dependency graph (#915).
Dependencies and verification
- Update the OTel metric SDK/HTTP metric exporter and gRPC/HTTP trace exporters to 1.47.0; update the AWS S3 SDK to 1.114.0 (#916–#920).
- The exact release source passed all 19 merged-main CI jobs and both CodeQL analyses. Release automation builds and smoke-tests Linux amd64/arm64, macOS amd64/arm64 and Windows amd64, then publishes SHA-256 checksums and Ed25519 detached signatures.
v0.3.44a
Wippy v0.3.44a brings a more precise Lua type checker, typed JSON decoding and deterministic JSON encoding, native exec confinement, reliable internode sessions, completion-aware boot services, faster registry replay and Lua cache loading, and cluster-aware naming on standalone runtimes. This release includes 46 merged PRs since v0.3.43a and uses go-lua v1.6.2.
Lua typing, linting, and JSON
Builtin module types and strict-any checking
Builtin module types now resolve in annotations and casts through the same environment used by linting, compilation, and the LSP. Qualified names such as channel.Channel<T> resolve directly; an unqualified exported type resolves when it is unique across modules.
wippy lint --strict-any opts into treating any as an unknown value that must be narrowed before use. The persistent setting is lua.type_system.strict_any; ordinary gradual checking remains the default. This helps prepare code for stricter typing, but does not turn the v1 runtime into v2 or automatically add runtime validation to every function call. (#840)
go-lua v1.6.2 includes recursive structural-subtyping, value/error correlation, metadata-preservation, inference and convergence improvements, plus fixes for several application-reported false positives. Expanded multi-return and vararg arguments are now checked value by value; optional parameters accept nil; assert accepts a string or structured Error message; and table.sort carries its element type. The v1.6.2 closure fix preserves declared field types on captured annotated locals. (#886)
The go-lua v1.6.0 release reported approximately 2x faster checking on its fixture corpus versus v1.5.x. That is a measured corpus result, not a promise that every application will lint or compile twice as fast. Whole dependency-graph copies during Lua compilation are also avoided. (#839)
Typed JSON decoding
json.decode(data, T) decodes and validates against a Lua type value, returning a statically inferred T? and an error. Invalid input reports a field path, such as $.age: expected integer.
local json = require("json")
type User = {id: string, age: integer}
local user, err = json.decode(data, User)
if err then return nil, err end
print(user.id)
This validates the decoded value at runtime; static typing describes the successful result, not the trustworthiness of arbitrary JSON before decoding. Untyped json.decode(data) is unchanged, and a nil optional type is treated as omitted, including the trailing nil in json.decode(req:body()). Callers should still handle req:body() errors explicitly. (#864, #880)
json.encode writes object keys in canonical order, making repeated encoding stable instead of depending on table iteration order. Consumers that relied on the previous incidental byte ordering should update golden files or signatures accordingly. (#871)
The existing typed process.listen(topic, {type = T}) surface continues to work and benefits from the improved checker. That listener option predates this release; it is not a new v0.3.44a API.
Cluster transport and process names
Reliable internode sessions
Internode transport now keeps sequenced frames in a resend window and resumes sessions across socket breaks, reconnects, simultaneous dialing, and connection replacement. While a peer remains a member and neither runtime restarts, sequenced frames are delivered exactly once and in send order. Gossip is unsequenced and does not have this delivery guarantee. (#860)
Departure, peer restart, session reset, or protocol failure ends the session and can discard outstanding frames. The ending side signals node-down after the last old frame is delivered and before a later session delivers frames. This is transport reliability, not durable messaging or exactly-once application side effects. A successful process.send still means local admission, not proof that a handler processed the request; use application acknowledgements and idempotency where needed.
Both sides can initiate links, gossip also travels over established internode connections, and topology events and remote monitor/link requests are carried across nodes. (#852, #853, #855)
Cluster upgrade requirement: upgrade all participating nodes together. The internode handshake and frame format change to protocol v3; there is no mixed-version compatibility path. Without TLS, post-handshake frames remain unauthenticated as before, so use TLS where transport authentication is required.
All name scopes on standalone runtimes
Applications can register LOCAL, EVENTUAL, CONSISTENT, and STRONG names even with clustering disabled. EVENTUAL uses its existing registry without peers; the global registry uses serialized in-memory KV, and STRONG waits for the local observer. Applications can declare their intended scope without branching on cluster availability. An enabled but broken cluster never silently falls back to standalone authority. (#888)
process.registry.lookup(name, scope) adds optional exact-namespace selection:
local process = require("process")
local pid, err = process.registry.lookup("cache", process.registry.LOCAL)
if not pid then return nil, err end
return process.send(pid, "get", {key = "example"})
An explicit scope never falls through on absence, an unavailable registry, or a read failure. LOCAL bypasses composed and parent lookup. Omitted or nil scope preserves global -> EVENTUAL -> LOCAL precedence. CONSISTENT and STRONG share the global namespace: selecting either does not strengthen read freshness or prove how the owner registered. Sending to a resolved PID does not re-resolve the name or attach a name-ownership fence.
Standalone registrations are process-lifetime, in-memory state. Their Go registration epoch is zero, not a durable fencing token. Enabling clustering takes effect on the next boot and does not live-migrate names.
Native exec confinement
exec.native entries can define an authority ceiling through a confine block. Lua launches may only narrow it with the confine option on executor:exec; they cannot choose new work roots, restore environment names, raise limits, re-enable networking, or weaken requested cleanup. Unsupported guarantees fail with CONFINE_UNSUPPORTED before the target executes, rather than silently degrading. (#862)
Enforcement is native on all three operating systems, but the available guarantees differ:
| Platform | Available enforcement | Important limits |
|---|---|---|
| Linux | Object-bound directory grants, private mount root, Landlock/namespaces/seccomp, total socket denial, delegated cgroup-v2 memory/task limits, wall timeout and process-tree cleanup; confined PTY support | Filesystem policy needs Landlock ABI 5; memory/task ceilings need delegated controllers. Confined explicit process groups are unsupported. Runtime-crash cleanup is not promised. |
| macOS | Verified signed Seatbelt trampoline, descriptor-bound work roots, environment ceiling, private home, wall timeout and singleton descendant prevention/cleanup | Linux-equivalent filesystem policy, total socket denial, aggregate memory/task limits, confined PTYs and process groups are unsupported. The Seatbelt entry point is deprecated by Apple; runtime-crash cleanup is not promised. |
| Windows | Fresh Less Privileged AppContainer, verified suspended launch and Job assignment, environment/private-home isolation, committed-memory and wall limits, singleton cleanup including runtime-process exit | Filesystem policy blocks, total socket denial, portable task limits, confined PTYs and process groups are unsupported. Writable work roots need dedicated, runtime-controlled low-integrity trees. |
This is not a portable promise of identical sandbox capabilities. Linux requires suitable kernel and cgroup configuration; Windows writable roots require host provisioning. Docker confinement, network allowlists/proxies, CPU/I/O quotas, persistent private homes, and launch-time widening are not included. See the exec specification for the entry and Lua surfaces, host requirements, and platform matrix.
Go 1.27 has an upstream Windows AppContainer compatibility issue: failed Winsock initialization can affect later Go file I/O. Arbitrary Go payloads combining failed networking with subsequent file operations may encounter it.
Boot, registry, and cached applications
lifecycle.startup: complete makes an auto-start supervised service a successful-completion barrier. Command entrypoints and tests wait for it; failure aborts execution instead of running against an incomplete boot. Services depending on it also wait for successful completion, rather than merely for process launch. complete requires auto_start: true. Normal required/optional startup behavior is retained. (#861, #865)
HTTP listeners wait for initial route registration before accepting traffic. This fixes initial route readiness; it does not claim that every ordinary serving path waits for every application boot-completion task. Updated process-service definitions are reconciled correctly. (#863, #844)
Registry metadata migration decodes released ownership forms before migration and rejects malformed or conflicting ownership transactionally. It prevents ownership loss during migration, but cannot reconstruct ownership already erased by a previously completed faulty migration; affected databases need history restored from a pre-1.1 backup. Lua snapshot state keeps canonical entry registry.owner/root metadata; no legacy provenance compatibility map is restored. (#865)
Lua snapshot state and plans expose recorded deployment pins as resolution.lock, while resolution.modules continues to describe the effective live graph. Live history may supersede lock selections without changing the recorded pins. Registry-wide permission registry.resolution.get controls resolution visibility; denied access omits that table. Historical snapshot APIs expose recorded resolution when available. (#884)
Standalone executables can carry Builder-produced warmed Lua caches. Seeds are checked against digest, schema, toolchain, source/dependency fingerprints, manifests, and checker settings; unusable optional cache data does not invalidate an otherwise valid deployment. This requires the matching Builder support and does not introduce a Lua SDK method. (#882)
Registry history replay now uses a private batch builder instead of copying the entire map for each operation. Lua fingerprints are memoized with graph/revision invalidation, and dumped artifacts use a bounded byte LRU while every load still undumps an independent proto. The byte budget is 32 MiB, capped by configured disk limits; the entry limit follows configured cache capacity, 20,000 by default. There is no separate 2,048-entry ceiling: applications with 10,000 small artifacts are supported, while larger working sets evict and fall back to disk. (#885)
Unchanged unpacked Hub modules are reused, artifact identity formats are normalized, transient Windows activation renames are retried, and options.unpack_modules overrides no longer rewrite the lock file. Qualified CLI module searches resolve exact org/module identities. (#874, #836)
Additional changes and upgrade notes
system.version()exposes the running binary's version to Lua without an operational-read permission. Development builds remain visibly stamped as development builds. (#849)- SQLite applications can opt into
foreign_keys: trueondb.sql.sqlite; enforcement applies to recycled connections too. The default remains off. (#848) - Actor termination during an executing step reports failure, but a later termination request does not overwrite an already-decided result. Process incarnation checks prevent terminating a reused pooled slot. (#881, #857)
- Scheduler wakeups avoid busy injection queues; shutdown retains log configuration confirmations; undelivered clock fires are reported; Lua error metadata survives the execution boundary; lint/checker panics are reported rather than hidden. (#838, #837, #859, #854, #858)
- The default Go memory limit is raised to 2 GiB; it is a Go GC pacing target, not an OS-enforced memory ceiling. Explicit deployment settings can still override it. (#835)
- AWS SDK/S3/SQS, compression, Tailscale, Jet, and PHP tree-sitter dependencies are updated. The patched ANSI replacement now lives under
internal/thirdparty, with no new SDK surface. (#845, #866-#870, #875-#879, #883)
Linting can now report genuine errors that older releases missed, particularly expanded multi-return arguments, optional values passed to required parameters, Error/integer values passed as strings, and un-narrowed any in strict mode. Run lint before upgrading application deployments rather than assuming a newly reported diagnostic is a runtime failure.
go-lua v1.6.2 still has known strict-mode false positives on some Bee patterns. This release does not claim to unblock every strict-lint application or include unreleased v1.6.3 fixes. Type manifests written by go-lua v1.6.x use encoding v14; older go-lua readers that lack v14 support cannot consume those manifests. Toolchain identity remains part of cache validation.
The release is built from the reviewed source tree of #888, which passed all 13 CI checks, including Ubuntu/Windows suites, native execution on Linux/macOS/Windows, and the required Linux confinement suite. Binaries are provided for Linux amd64/arm64, macOS amd64/arm64, and Windows amd64, with checksums and detached signatures. Framework changes, new function-handler argument-validation work, and unreleased go-lua fixes are not included.
v0.3.43a
Cluster naming and Strong claims; application launch, packaging and sealed WAPP publication; terminal and exec improvements; Lua and WASM cache and hot-update work; registry, boot and reliability fixes.
v0.3.42a
Fix source-owned local dependency removal on restart while retaining immutable artifact checks; authenticate CLI module search for private modules.
v0.3.41a
Focused maintenance release: restores local replacement trees and history consistently, improves replacement diagnostics, redacts PostgreSQL credentials, fixes HTTP/WebSocket and SQLite behavior, and hardens shutdown, naming, KV and Raft lifecycles. Includes go-lua v1.5.21 and the existing WASM mounted-file read fix. Does not include WASM actors or Builder/Bee application-launch APIs.
v0.3.40a
Adds SQLite change-data capture alongside PostgreSQL CDC, hardens capture contracts and shutdown behavior, fixes terminal navigation keys and bounded canvas rendering, and expands CI with native Lua application and real CDC integration coverage.
v0.3.36a
Fixed event bus unsubscribe so it now stops any further deliveries immediately after unsubscribing.
v0.3.35a
Fix workspace replacement lock reconciliation and diagnostics; expose HTTP request headers to Lua; migrate to Go 1.27 and refresh runtime dependencies.
v0.3.34a
Added Pyodide/WASM runtime support with instance-owned TCP sockets and better socket deadlines, plus fixes for Docker volume handling, async call cancellation, shutdown sync, and legacy state migration.