This file is no longer maintained. The live changelog is
site/src/content/docs/book/reference/changelog.md, published as Version compatibility & changelog.cargo xtask stampwrites a row there for every merged increment, from itsdesign/pending/file. The entries below are kept as history: the Karn → Bynk rename and v0.142.0.
The project, its toolchain, and its in-language surface were renamed from Karn to Bynk. This is a breaking change for existing sources.
- The compiler binary
karncis nowbynkc; the driverkarnis nowbynk(e.g.bynk dev,bynk test,bynk doctor). The language serverkarnc-lspis nowbynkc-lsp. - The project manifest
karn.tomlis nowbynk.toml. - The source file extension
.karnis now.bynk. bynk deployprovisions the KV namespace required by a Cloudflare Worker, stores its id in committedbynk.deploy.lock, and pushes through Wrangler. It supports a non-mutating--dry-runplan, JSON output, confirmation /--yes, and idempotent re-deploys (#583).bynk deployships every context of a project, ordered so that each Worker is uploaded after the Workers it binds to — Cloudflare resolves a Service Binding at upload and rejects one whose target does not yet exist, so the order is a correctness requirement. It previously refused any project with more than one context.--context NAMEre-pushes one context and reports (rather than pushes into) a dependency that was never deployed. A failure stops the run and keeps what landed; a re-run resumes in the same order (#601). Breaking (pre-1.0):--format jsondescribes every context, so the top-levelworker,kv, anddeployfields are replaced by acontextsarray alongside the resolvedorder— read.contexts[0].workerfor.worker, and.contexts[0].action(deployorredeploy) for.deploy, which was alwaystrue.bynk deployprovisions every Cloudflare resource a context declares, not only KV. Aservice … from queue("n")has its queue created — by name, before the push, becausewrangler deploywill not create one for you — it checks and fails with "To create it, run: wrangler queues create" — and anagent's Durable Object migration is applied bywrangler deploy. Each queue is checked against Cloudflare before every deploy and created only if absent, so a queue deleted outside Bynk comes back on the next one. Migration state is Cloudflare's, andbynk.deploy.lockrecords none of it, so the plan'smigration <tag>line states what the push will ask for and is marked advisory;bynk deploycorrespondingly cannot report migration drift. The plan gainsqueue create|reuse <name>andmigration <tag>lines (in--format json, aqueuesarray and amigrationobject per context). The lock file gainsenvironments.<env>.queues, a list of created queue names, additively — an existing lock file loads unchanged, and the version stays1. CI's refusal to create an unrecorded resource still covers KV alone: a queue is addressed by the name in your source, so CI creating one strands nothing (#600).bynk devserves every context of a multi-context project at once, with the Cloudflare Service Bindings between them wired, so cross-context calls resolve locally — onewrangler devper context, connected through wrangler's dev registry. It previously served one context and failed a multi-context project as ambiguous.--contextis now repeatable and narrows to a subset; each context gets its own port from--base-port(and its own inspector port from--inspect-portunder--inspect), so--portis no longer accepted through the--passthrough where the driver allocates it (#552).
bynk fmt/bynkc fmtnow honours the 100-column target across the board (#963). Every fit test measures the whole line — the column a construct starts at, and the signature tail, closing paren or arm comma that follows it — where it previously measured the construct alone against the indent, so a body behindfn name(params) -> Retwas judged to fit and then overran. Long constructs now go vertical: record constructions and list literals one entry per line, parameter and argument lists one per line,&&/||runs broken before each operator,.-chains broken before each call (unless a trailing argument can open its body on the chain's own line), andifbranches split. Reformatting existing sources will produce diffs. The target stays soft — a line with no legal break point in it (a long string literal, aMatches("…")regex) is left long rather than mangled.bynk fmt/bynkc fmttake style flags that override the canonical style for one run (#968):--indent tab|spaces,--indent-width N,--max-line-width COLUMNS, and--trailing-comma/--no-trailing-comma. Defaults are unchanged, so an invocation that passes none formats exactly as before.--checkjudges each file against the style the run asks for, so a project on a non-default style gets a working CI gate. Both binaries flatten one sharedFmtArgs, andbynk fmtforwards the flags to aBYNK_BYNKC- pinned compiler rather than silently formatting to the canonical style.--indent-widthalongside tab indentation is an error, not a no-op.
The reserved namespace karn is renamed to bynk. Update your sources:
consumes karn { … }→consumes bynk { … }- platform adapters
karn.cloudflare/karn.node/ … →bynk.cloudflare/bynk.node/ … - stdlib units
karn.list/karn.map/karn.string→bynk.list/bynk.map/bynk.string
- Every diagnostic code is reprefixed
karn.*→bynk.*(e.g.karn.namespace.reserved→bynk.namespace.reserved). - The internal HTTP dispatch prefix
/_karn/is now/_bynk/and the cross-context caller headerX-Karn-Calleris nowX-Bynk-Caller.
- The editor's project analysis (
bynkc-lsp, backing diagnostics, go-to-definition, and completion) silently omitted five categories of whole-project checking it previously reported (P4.2, #1122 —bynk-idenow reachesbynk-check/bynk-projectdirectly instead ofbynk-emit, and the new entry point did not yet port these five):messages-bundle validation, locale-bundle ambiguity, event-subscription validation, function-type-boundary checks, and everything inside asuite/test integrationbody (diagnostics and the binding index, so go-to-definition/find-references/completion for a test-only binding regress too).messages-bundle validation, locale-bundle ambiguity, event-subscription validation, and function-type-boundary checks are restored (P5.0/P5.1/P5.2, #1128/#1130/#1126,design/tracks/semantics-in-the-checker.md§6) — all four now run insidebynk-check::analysis::analyse_projectitself, not just at build time. Everything inside asuite/test integrationbody is restored too (P5.4,design/tracks/semantics-in-the-checker.md§6) — diagnostics and the binding index both, sinceprocess_tests'/process_integration_tests' checking half now runs insideanalyse_projectas well (bynk_check::test_suites::phase_test_bodies/phase_integration_bodies), so go-to-definition/find-references/completion for a test-only binding work again too. Schema-registry reconciliation and platform-lock enforcement now originate inbynk-checktoo (P5.3,design/tracks/semantics-in-the-checker.md§6), but are not newly affected observably — both were, and remain, unreachable from the editor's analysis (the editor never reconciles the schema registry, and always analyses as the default Cloudflare platform/Bundle target, under which platform-lock can never fire since Cloudflare is the only platform-native unit that exists today). A malformedbynk.schema.lock(bynk.project.schema_registry_corrupt) and aSecrets.getcall with a computed argument (bynk.secrets.computed_name) now originate inbynk-checktoo (P5.5, same track) — also not newly affected observably, for the same reasons (no on-disk schema lock and a Workers-only gate, respectively, neither reachable from the editor's hardcoded Cloudflare/Bundle analysis).
- Rename
karn.toml→bynk.tomland every*.karnsource →*.bynk. - Replace
consumes karnwithconsumes bynkand everykarn.<platform>/karn.<stdlib>reference with itsbynk.<…>equivalent. - Recompile with
bynkc(orbynk build).
Two features ship together (ADRs 0165 & 0166).
A body-taking route (POST/PUT/PATCH) can now bound its request body size:
- A service declares a byte ceiling with a
limits { maxBody: <Int> }section in header position (besidecors { }/security { }), overridable per route with a@limit(maxBody: <Int>)handler annotation (the@cacheplacement; valid only onPOST/PUT/PATCH). - A capped route rejects a request whose
Content-Lengthexceeds the cap with a synthesised413 PayloadTooLarge({ kind: "PayloadTooLarge", details: … }), produced before the body is read and before theby/Bearer auth seam — the boundary posture of the method-semantics405. It reuses the existing413status, so the closedHttpResultregistry is unchanged; the413is CORS/security-header-stamped so a cross-origin caller can read it. - Precedence is route
@limit→ servicelimits→ none. With neither, a route has no cap and emits byte-for-byte unchanged output — the feature is opt-in. - Enforcement is a
Content-Lengthfast-reject (not a hard guarantee — the header can be absent for a chunked transfer or spoofed), pairing with the Workers platform cap. A streamed-read cap is a named follow-on. maxBodyis a positiveIntbyte count (26_214_400for 25 MiB). A byteSizeliteral (1.mb) is a named follow-on.
Int and Float literals now admit an _ digit separator between digit groups
(1_048_576, 1_000.5) — never leading, trailing, or doubled. The separators are
stripped before the value is parsed (purely visual), and the as-written lexeme is
preserved so bynk fmt keeps the author's grouping. Motivated by maxBody's large
byte counts, but applies language-wide.
Closes #494.