Repository navigation
Releases
SSPUR 1.0.0
SSPUR 1.0 is stable: code that works on 1.0 keeps working on every 1.x release. Builds for macOS, Linux and Windows, plus the VS Code extension, the JetBrains template and the Zed extension, all signed with build provenance.
- The 1.x promise. The language, the standard library,
run,test,check,fmt,build,depsandpublish, the package and lock formats, content hashes, error codes and the language server are stable. GPU kernels, deploy, bare-metal targets, the LLVM backend, sync, PGO, the Cranelift JIT and the agent tooling stay experimental and are marked so insspur --help. The stability policy says what that means, and a compatibility suite in CI holds every release to it. A feature is deprecated withW_DEPRECATEDbefore 2.0 can remove it. - Moving from 0.14:
sspur fixrewrites what 1.0 reads differently, so the program means what it meant, and lists what to check by hand. The migration guide covers every change with before and after. - Safer:
sspur checkproves that everyround,floor,ceilandtruncon anF64gets a value inInt's range, from refinements,pre, guards and loops, or warns withW_CONVERSION_MAY_TRAPand the fix. The proofs are cached by content hash, so checking unchanged code proves nothing again. - Lighter: the macOS binary lays out its hot paths first, so peak memory is 10 to 20% lower on every command (hello world 2.5 to 2.0 MB against 0.15.0). The playground's download is 5 KB smaller.
- Signed releases: every file attached to this release has a build provenance attestation, and each
sspurarchive has a CycloneDX SBOM. Check one withgh attestation verify FILE --repo sspurlang/sspur.install.shchecks it for you whenghis installed and signed in. - Fixes:
sspur deploy localno longer leaves a request waiting through a hot swap, an interrupt handler on the QEMU targets no longer nests when it re-arms the timer, the Zed grammar reads 0.15 strings and scoped names, and the reference now documents every builtin, flag and environment variable.
Startup, the native benchmarks and content hashes are unchanged from 0.15.0, and every published package passes the 1.0 checks.
Full list in the changelog.
SSPUR 0.15.0
sspur fmt keeps your comments, the playground checks as you type about 5 times faster, and the interpreter builds lists and maps in linear time. This is the release candidate for 1.0: it settles the last syntax and semantics questions. Builds for macOS, Linux and Windows, plus the VS Code extension, the JetBrains template and the Zed extension.
- Comments survive formatting (ADR 0042):
sspur fmt, the language server, the playground's Reformat and the registry'sbuild.fmtkeep every comment where it was, and break a line around a comment the way they break long lines. - Faster:
- the playground checks an edit about 5 times faster, and its download is 2.5% smaller;
sspur checkis about 4% faster andsspur fmt3% faster, and both use less memory;- in the interpreter,
xs := xs.push(x),m := m.put(k, v)andb := b.add(s)append in place, so 40,000Map.putcalls take 0.1 s instead of 15 s.
- Language:
- typed lambda parameters
(x: Int) => x + 1,_ => e, and binding annotationsxs: List[Int] = []that are checked and kept; pkg.module.Namewithout ause;- type aliases over effect rows
type Handler[e] = ...; json.decode[T]anddb.query[T]inside generic functions;- operators on
BigInt,DecandRatio; \u{...}escapes.
- typed lambda parameters
- Standard library:
Opt.and_then,Res.and_then,List.repeat,Map.is_empty,req.peer(the client's address), and binary responses withhttp.bytes. - New diagnostics:
W_UNUSED_RESULTflagsxs.push(x)on a line of its own, which did nothing;- a note when a path dependency changed since
sspur.lockpinned it; - dependency audits are shown at the dependency's own lines.
- Deeper recursion on request:
SSPUR_STACK=256M sspur run --interprecurses as deep as that stack allows, about 130,000 calls. Without it, the limit stays 20,000 calls. - Fixes: effect rows shared by several lambdas,
fail[E]in parameter rows, catch-all| _ =>over several error types,db.txin generic code, effects in package signatures,to_bitsnatively, a native compiler stack overflow, nestedmatcharms, andcatchinside a string.
Changes that can affect existing code:
- In a string,
{interpolates only an expression that isn't a lone literal. So"a{3}"and"{{x}}"are text, and"{"no longer swallows the code after it. withbinds tighter than comparisons.-0.0 == 0.0, andround,floor,ceilandtrunctrap on NaN, infinities and overflow instead of saturating;checked_roundand the otherchecked_*functions return anOpt.==uses a type's ownEqimpl inside lists, options, results and tuples.
Content hashes and published packages are unchanged. Startup, memory and the native benchmarks are unchanged or better than in 0.14.0.
Full list in the changelog.
SSPUR 0.14.0
sspur fmt breaks long lines, and the playground reformats code like an IDE. Builds for macOS, Linux and Windows, plus the VS Code extension, the JetBrains template and the Zed extension.
orders = [
Order{customer: "ana", item: "pen", cents: 300},
Order{customer: "bo", item: "ink", cents: 900},
]
- Line breaking (ADR 0040): lines over 100 columns break at their natural points: lists, records, calls and parameter lists one element per line,
and/orchains one condition per line, longifchains and method chains. Short lines stay as they are. Formatting never changes a definition's content hash or a package's export hash, so lock files and published packages keep verifying. - Playground: Reformat Code (Cmd+Option+L, Ctrl+Alt+L or Shift+Alt+F) for the whole file or the definitions a selection touches, keeping the cursor in place; a marker at column 100; smart indentation as you type; an optional format on run. Phones scroll long lines sideways. The download is 3 KB smaller.
- Public modules: a package can mark a module
public = true, and dependentsuse pkg.models.{Doc}. - Fixes:
- a function imported with
useno longer hides a record field of the same name; p.0.1reads as two tuple accesses;- escapes apply inside literal braces in strings;
sspur qnames scoped dependencies by their key.
- a function imported with
- Registry site: README tables, the top 12 packages, capped descriptions, and a 404 page that suggests the closest package.
Startup, memory and the native benchmarks are unchanged from 0.13.1.
Full list in the changelog.
SSPUR 0.13.1
SSPUR has a new home: the GitHub organization sspurlang and the site sspurlang.github.io. Builds for macOS, Linux and Windows, plus the VS Code extension, the JetBrains template and the Zed extension.
- New addresses: the repository is github.com/sspurlang/sspur, the docs, playground and Pulse are at sspurlang.github.io/sspur, and the registry is at sspurlang.github.io/sspur-packages. Old repository, release and raw URLs redirect, and the old Pages addresses forward to the new ones. Existing installs, lock files and links keep working.
sspurnow reads the registry from its new home. The Homebrew tap staysutkarshavardhana/sspur. - Registry:
- A public member of an organization may publish the first version of a package from the organization's repositories.
- A package whose repository was transferred or renamed on GitHub keeps publishing from its new home.
- The check
pkg.forkstops a fork from claiming a new flat name. - The hourly sweep reruns unanswered requests one at a time; during bursts GitHub had cancelled all but one queued run.
- 23 packages are published, among them strings, lists, dates, validate, semver, jwt, webhooks, ratelimit, cache, jobs, sqlkit and middleware.
Startup, memory, the native benchmarks and the playground download are unchanged from 0.13.0.
Full list in the changelog.
SSPUR 0.13.0
Scoped package names, big multi-module packages, and credit for authors in the registry. Builds for macOS, Linux and Windows, plus the VS Code extension, the JetBrains template and the Zed extension.
sspur add acme/pkg # pkg = { package = "acme/pkg", version = "^1.0.0" }
sspur add globex/pkg --as gpkg # two scopes' pkg in one program
sspur add pagination@0.1.1 # exactly 0.1.1; ^ and ~ take ranges
sspur deps tree # which version of each package you use
- Scoped names (ADR 0039):
acme/pkg, whereacmeis the GitHub user or organization that owns the repository. A new blocking check,pkg.scope, verifies it. The request must come from that user, or from a public member of the organization. Two companies can each havepkg, and nobody can publish under someone else's scope. Flat names likepaginationwork as before. - Big packages: a published package can contain its own modules, and every check covers every module. Limits are now 2000 files, 1 MiB per file, 4 MiB of source and 20 MiB per repository, room for a library the size of Apache Commons Lang. Shared modules are built once and cached by their inputs. A cold
sspur checkof a 2.5 MB package in 200 modules went from 166 s to 11 s. - Credit for authors: the index commit for a published version is authored by the person who requested it, with a
Co-authored-byline, so it counts in their GitHub contributions. - Exact versions:
sspur add name@1.2.3now means exactly 1.2.3. Use^1.2.3for the previous meaning.
A 0.12 client keeps working with flat-name packages; scoped packages need 0.13. Startup, memory, the native benchmarks and the playground download are unchanged from 0.12.1.
Full list in the changelog.
SSPUR 0.12.1
Fixes from the first days of the package registry. Builds for macOS, Linux and Windows, plus the VS Code extension, the JetBrains template and the Zed extension.
sspur updatemoves to newer versions. A registry dependency now updates to the newest version its requirement allows, even when the exports didn't change, and says so:~ pagination 0.1.0 -> 0.1.1 (no API change).sspur fmtkeeps record shorthand.Page{items, total, limit: r.limit}stays as written, andx: xprints asx. The registry's format check also accepts continued lines,elseon its own line and indented match arms, so idiomatic code publishes.- Programs find the
sspurthat started them.sspur run,testanddebugsetSSPURto their own executable, so the web app's tests use the same binary even when an older one is on PATH. - Windows: a native build could read another build's half-written library name and fail with
LoadLibraryExW failed. Fixed, and loads now retry briefly and keep the system's reason. - Registry: the first version of a new package name waits for a maintainer's
/approve, and later versions merge on their checks alone. The site has proper desktop and phone layouts.
Startup, memory, the native benchmarks and the playground download are unchanged from 0.12.0.
Full list in the changelog.
SSPUR 0.12.0
A package registry that runs entirely on GitHub at no cost (ADR 0038), and sspur new web-app, a production-shaped reference application. Builds for macOS, Linux and Windows, plus the VS Code extension, the JetBrains template and the Zed extension.
sspur new web-app notes && cd notes
sspur run tests/run.ssp # unit, end-to-end, concurrency and load tests, both tiers
sspur run # http://127.0.0.1:8080
sspur add pagination@^0.1
- Registry (sspur-packages): one JSON file per package, each version pinned to its tag, commit and the content hash of its exports, with a reproducible source snapshot kept as a release asset. Installs use the snapshot and verify it, so a deleted repository or a moved tag never changes what you get. Installing never runs package code.
sspur add name@^1.2,update,publish,yank,search,source: Cargo's requirement syntax with the one-version rule, offline and reproducible from the lock.- 35 publish checks in SSPUR: build and tests in both tiers, semver by API diff, an effects audit that needs new capabilities acknowledged, docs, licence, naming, size and secrets, and a written code-review standard. Each failure gives file, line and the fix.
- The publish loop: a request becomes a pull request whose description lists every check. Push a fix or comment
/recheck, and it merges when every blocking check passes. Only package entries merge automatically. - Publish page: pick your repository and tag, run the checks in the browser, and open the request on GitHub. No sign-in or tokens.
sspur new web-app: a notes service with accounts, about 800 lines of SSPUR. It has a JSON API and HTML pages through onesvc, SQLite migrations and full-text search, Argon2id, hashed session tokens, CSRF protection and login rate limits. It ships with tests, JSON logs, backups, and a systemd unit plus a Caddyfile for deployment. The tutorial walks through it.- Language and library fixes the app drove:
svc api(app: App)parameters, and svc endpoints that take and return any JSON type;whererefinements checked after JSON decoding, andSecret[Str]inconfig.load;req.formandreq.cookie, JSON log fields, andproc.spawn;- in-tree module packages that follow their source;
- the interpreter's
catchno longer swallowing other error types; - debug line mapping for networking and library programs.
Startup, memory and the native benchmarks are unchanged from 0.11.0. The playground download is 753 bytes larger (973 KB gzipped), for the catch fix.
Full list in the changelog.
SSPUR 0.11.0
Databases, crypto, compression and CSV, the data and security libraries real applications need (ADR 0037). Builds for macOS, Linux and Windows, plus the VS Code extension, the JetBrains template and the Zed extension.
type User = {id: Int, name: Str}
fn main() -> Unit ! db, rand, log, fail[DbErr]
= do
c = db.open("app.db")
_ = db.migrate(c, ["create table users(id integer primary key, name text unique)"])
_ = db.exec(c, "insert into users(name) values (?)", ["ada"])
us = db.query[User](c, "select * from users where name = ?", ["ada"])
h = crypto.hash_password("correct horse")
log("{us.len} {crypto.verify_password("correct horse", h)}")
- SQLite (
dbeffect): typed queries that map columns to records by name, parameters only by binding, transactions with savepoints, migrations, WAL and a busy timeout by default, and a connection pool that the HTTP server's concurrent handlers can share. Bundled as source, so there is no system dependency. - Crypto: SHA-256/512, BLAKE2b, HMAC, XChaCha20-Poly1305, Argon2id password hashing, Ed25519 signatures, secure random tokens and keys (
randeffect), constant-time compare, UUID v4/v7. Secrets never print their contents. Checked against NIST and RFC test vectors. - Compression: gzip, deflate and zstd, for bytes and streamed files. The HTTP server's gzip now uses miniz.
- CSV: RFC 4180 reading and writing, typed decoding into records by header name, and streaming over large files.
Bytes: a byte string with hex and base64 forms and binary file reads and writes.
Libraries, all vendored as source and compiled on first use: SQLite (public domain), Monocypher (BSD-2/CC0), libsodium SHA-2 parts (ISC), miniz (MIT), zstd (BSD). Programs that don't use them don't load them; startup, memory, the native benchmarks and the playground download (972 KB gzipped) are unchanged from 0.10.0.
Full list in the changelog.
SSPUR 0.10.0
Networking and source-level debugging, so SSPUR can build and debug real network services. Builds for macOS, Linux and Windows, plus the VS Code extension, the JetBrains template and the Zed extension.
Networking (new net effect, ADR 0035)
Client
fn title(base: Str, id: Int) -> Str ! net, fail[HttpErr]
= json.decode[Todo](http.fetch("{base}/todos/{id}")).map(t => t.title).or("?")
http.get,http.postandhttp.send(req)returnRes[Response, HttpErr](Dns,Connect,Tls,Timeout,HttpStatus,Io);http.fetchraises for anything but 2xx.- Timeouts, retries with backoff and
Retry-After, redirects, downloads to a file, proxies from the environment, gzip, and certificate verification on by default. - libcurl on macOS and Linux, WinHTTP on Windows.
Server
svc api
ep get "/todos/{id}" = todo
ep get "/todos" = todos
ep post "/todos" = add
fn main() -> Unit ! net, log, env
= http.serve_with(http.server(8080) with gzip := true, health := "/healthz", api)
- Routes come from
svc: path and query parameters and JSON bodies bind and are checked as in deployed services. - CORS, gzip, static files, request size limits, timeouts, request ids, keep-alive, TLS (OpenSSL 3), graceful shutdown on SIGINT/SIGTERM, and concurrent handlers on a thread pool.
- About 28,000 requests a second on an M1 Pro, with memory flat under sustained load; the request parser is fuzzed under AddressSanitizer.
Essentials: config.load (environment, then a JSON file, then defaults, checked by contracts), log.debug/info/warn/error with SSPUR_LOG_LEVEL and SSPUR_LOG=json.
Both tiers give byte-identical responses. Programs that don't use networking don't load it. In the playground, HTTP returns a typed error.
Debugging (ADR 0036)
sspur run --debug builds native code that lldb and gdb step through at the .ssp level: breakpoints by file and line or by function name, SSPUR names in backtraces and locals, readable strings, lists, records and sums, and a sspur_trap breakpoint for failed contracts. sspur debug launches the debugger; sspur debug-init installs the formatters; VS Code works through CodeLLDB. Release builds are unchanged.
Also
- SSPUR Pulse fetches with the new client instead of curl.
- Startup and memory: the cached
sspur runpath does no extra work, and peak memory is below 0.9.0 on Apple Silicon. The playground download is smaller than 0.9.0's (972 KB gzipped). - Native benchmarks are unchanged.
Full list in the changelog.
SSPUR 0.9.0
SSPUR Pulse, a live dashboard of the world computed by SSPUR programs, plus two compiler fixes it uncovered and an up-to-date set of docs. Builds for macOS, Linux and Windows, plus the VS Code extension, the JetBrains template and the Zed extension.
SSPUR Pulse
https://utkarshavardhana.github.io/sspur/pulse/
Eight boards, recomputed every 6 hours by SSPUR programs on free GitHub Actions and served from GitHub Pages: what the world is reading on Wikipedia, upcoming space launches with a live globe of about 16,700 satellites moving at their real speed, earthquakes, the fastest-rising GitHub repos, a Hacker News time machine, the day's AI papers, weather in 135 cities, and public holidays in more than 200 countries. Charts you can toggle, your own local weather, light and dark themes, and a few secrets to find. No servers, no keys, no trackers. The source is in pulse/ and how it works explains the design.
Fixed
- Native
json.decodegave every decoded string room for the rest of the input, so large arrays used memory quadratic in their size: 1.3 MB of JSON ran out of an 8 GB heap. It now decodes in linear memory and about 3x faster than the interpreter. Pulse runs natively because of it. g.OBJECT_NAME, a field whose name starts with a capital letter, parsed as a package-qualified name. It is a field access unlessgis a dependency, so JSON records with upper-case fields work.
Docs
The README, docs site, benchmark pages and editor docs are current: native code at 0.14x to 0.89x of C++'s time with memory near or below C++'s except on the GPU benchmark, the large-codebase agent results (Sonnet 0.59x TypeScript's tokens, Opus 0.85x, Haiku 0.68x), while e is p, the cached run and the Zed extension.
Full list in the changelog.