The reference client for the Blygger protocol — a Cloudflare Worker that publishes a blyg, subscribes to others, and threads, transcludes and responds across them.
Protocol implemented: blyg 0.3, level 2 · Client version: 0.10.0 ·
generator: blygger-studio/0.32.2 · releases + upgrading
The Blygger spec defines the protocol. As of 2026-09-28, at least seven clients publish live blygs. Six are other implementations. Blygger uses static files and RSS. You can write your own client if this client's choices do not suit you. Several people already do.
- Studio is the private interface for composing fragments and threads, hoppers, subscriptions, signals, and TK generation. The protocol leaves these features to each client. Other clients can implement them differently.
- Page is the public protocol output: manifest, feed, item documents, permalinks, pinned snapshots, and archives. The Worker serves this output directly. You can also export identical bytes to a static host. Identical output is a protocol requirement.
Use the latest GitHub release for a live deployment. Release downloads start with Studio 0.9.0. Older releases only have GitHub's source archives. You need:
- Node.js 22.18 or newer.
- A Cloudflare account with D1 and R2 enabled.
- A domain on that account to use a custom domain.
SDK generation uses a pinned Hey API npm package.
The Worker limits API work, AI calls and stored OAuth clients. Outbound fetches allow public destinations by default. See security configuration for defaults and the explicit LAN opt-in.
Draft uploads require an owner cookie or an API token with owner:read.
Studio sends the cookie for previews. Publication makes used images public and
keeps images needed by published snapshots and pins available.
Contributors: see tests and their limits for verification commands, oracle replay, and the upgrade test.
Download blygger-worker-VERSION.tar.gz and SHA256SUMS from the same release.
Replace VERSION below with the release number, without the v prefix.
shasum -a 256 blygger-worker-VERSION.tar.gz
# Compare the result with this file's line in SHA256SUMS.
# On Linux, use sha256sum if shasum is unavailable.
tar -xzf blygger-worker-VERSION.tar.gz
cd blygger-worker-VERSION
npx wrangler@4 login
npx wrangler@4 whoami
npx wrangler@4 d1 create blyg-myname
npx wrangler@4 r2 bucket create blyg-myname-mediaCheck the account in whoami. Edit the included wrangler.jsonc:
- Set
nameto your Worker name and add youraccount_id. - Set the D1 database name and ID from
d1 create. Keep its binding namedDB. - Set the R2 bucket name. Keep its binding named
MEDIA. - Add
routes: [{ "pattern": "blyg.example.com", "custom_domain": true }], using your domain. Keepvars.MOUNTas""for a subdomain. - Keep
main: "worker.js",no_bundle: true, andmigrations_dir: "migrations".
Then provision the schema, set secrets through Wrangler's prompts, and deploy:
npx wrangler@4 d1 migrations apply DB --remote
npx wrangler@4 secret put OWNER_PASSWORD
npx wrangler@4 secret put COOKIE_SECRET
npx wrangler@4 deployUse a strong owner password and a separate random cookie secret (at least 32 random bytes, such as 64 hex characters generated by a password manager). Keep both out of the config. To check the setup:
- Open
https://blyg.example.com/studio. - Sign in.
- Set your title and author name in Settings.
- Add subscriptions.
- Publish a fragment.
AI is optional. Each AI function (TK generation, changelog notes, and later
feed scoring) takes its own model in Settings → AI models, chosen from
models.json. Set the key for each provider you use: AI_PROVIDER_KEY
(Anthropic), OPENAI_API_KEY (OpenAI) or GOOGLE_AI_KEY (Google), e.g.
npx wrangler@4 secret put AI_PROVIDER_KEY. To change the model list without
upgrade conflicts, copy entries into a models.local.json (gitignored, same
shape; see the comment in models.json) and redeploy.
The archive includes the Worker bundle, migrations, licenses, and a deployment
README. It needs no npm ci or source build. Wrangler's
Cloudflare setup instructions
cover login and bucket creation.
Choose this path to change Studio or use its provisioning and export scripts.
Replace vVERSION with a published release tag.
git clone --branch vVERSION https://github.com/blygger/blygger-studio.git
cd blygger-studio
git switch -c my-blyg
npm ci
npx wrangler login
npm run init
npm run deploynpm run init asks you to choose the account and domain, creates D1 and R2,
writes deployment config, applies migrations, and sets secrets. It can resume
after a failed step. It does not deploy. npm run deploy does that afterward.
Keep your deployment config in your own checkout. Contributors should use the
ignored wrangler.private.jsonc. Build before a direct Wrangler deploy:
npm run build
npx wrangler deploy --config wrangler.private.jsoncKeep the committed template generic.
To export an existing site's public pages for a static host:
npm run export -- --out exported-blyg --base https://blyg.example.com/Clone the repository and run npm ci. Create an ignored .dev.vars file with
local-only OWNER_PASSWORD and COOKIE_SECRET values, then run:
npx wrangler d1 migrations apply DB --local
npm run devOpen http://localhost:8787/studio and sign in with your local password. Local
D1 and R2 data are separate from your live deployment. Tests use their own
in-memory fixtures and do not need .dev.vars or a Cloudflare login.
npm run build
npm run typecheck
npm run test:ui -- --maxWorkers=2
npm test -- --maxWorkers=2
npx playwright install chromium
npm run test:e2ePublic HTML pages use a shared Cloudflare cache for 60 seconds. After expiry, Cloudflare can serve the saved page for another 300 seconds while it refreshes in the background. Browsers validate their saved HTML with a content ETag. Changes, including withdrawals and collection visibility, can appear after this cache window. Studio, API, media, and XML routes keep their existing behavior.
New installations and Worker archives include the cache configuration. Existing installations must add this block to their deployment config, including any ignored private config, and deploy with Wrangler 4.107.0 or later:
"cache": { "enabled": false },
"exports": {
"PublicHtml": { "type": "worker", "cache": { "enabled": true } }
}The default router stays uncached. Only public HTML GET/HEAD requests enter the
cached PublicHtml entrypoint. Its cache key includes the full origin and path,
so aliases do not share origin-dependent HTML. Browser reloads validate the saved edge copy without forcing another render.
Query parameters do not change public HTML and do not create separate cache entries. Errors and missing pages
use no-store.
No migration, dashboard cache rule, extra binding, or scheduled job is needed. Without the config block, ETags still work but each request renders the page. Local Wrangler and native tests exercise routing and validators. Cloudflare's production cache supplies shared hits and background refresh.
After deployment, inspect Cf-Cache-Status for HIT or UPDATING. Test repeat
GET requests. If-None-Match with the returned
ETag should produce a bodyless 304. See Workers Cache configuration.
{mount}/webmention accepts POST requests without a password. Another blyg uses
this endpoint to report that it quotes or responds to one of your items.
The Worker checks that the source item names your item. It stores no source
content.
Webmention is optional at every level of protocol 0.3 (§15). Disable Settings → Accept Webmentions to remove it. The Worker then omits the manifest key and page links, and the endpoint returns 404. Your blyg still sends mentions when you quote other people.
When enabled, hourly rate limits apply to each source host, each registrable
domain, and the endpoint as a whole. See
src/mentions/store.ts for the limits and their reasons.
Every release has a v{version} tag and a
changelog entry with an explicit Migrations: line. Read all
entries between your installed version and the target before upgrading.
Coming from 0.8.x or earlier? 0.9–0.11 rebuilt the Studio and changed the owner API. Read Upgrading to 0.11 first. It covers both upgrade paths, path mounts, checks, rollback, and the old-to-new route table for third-party tools. Your running version is the
generatorinhttps://blyg.example.com/blyg.json(for a path mount, include that path beforeblyg.json).
Download assets from the same release so the SDK and contract match the Worker.
| Download | Use |
|---|---|
blygger-worker-VERSION.tar.gz |
Deploy the bundled Worker with migrations and generic config. See installation. |
blygger-openapi-VERSION.json |
Import into an OpenAPI viewer, API tool, or client generator. |
blygger-sdk-SDK_VERSION.tgz |
Install the JavaScript/TypeScript SDK in your app. |
release.json |
Check the Studio, SDK, and API versions, source commit, and generator pin. These versions are independent. |
SHA256SUMS |
Compare the SHA-256 digest of each downloaded file before using it. |
Install the SDK archive, then use it from an app on the same origin as Studio:
npm install ./blygger-sdk-0.1.1.tgzimport { BlyggerApi, createBlyggerClient, unwrap } from "@blygger/sdk";
const client = createBlyggerClient({ baseUrl: window.location.origin });
const reading = await unwrap(BlyggerApi.listReading({ client, query: { offset: 0, limit: 25 } }));Sign into /studio first: API calls use its owner session cookie. The SDK does
not create a login session. Node clients must supply a session cookie in
headers or a custom transport. See SDK usage. Never embed an
owner cookie in browser source. Cross-origin clients can use resource-bound bearer tokens from Studio’s access page. See client access for scopes, revocation, and the mounted OAuth/MCP discovery profile. Install the downloads locally. The SDK is not published to the npm registry.
The downloaded OpenAPI file describes /api paths. Its default server is
http://localhost:8787. Select your deployed origin in the API tool or generator.
The deployed contract is also available to a signed-in owner at
/api/openapi.json. Downloading the spec does not grant access to the API.
See the owner API guide for resource routes, partial edits, creation, and pagination.
Interface experiments that should not become the reference design ship as
Studio extensions: first-party code an operator compiles
in with extensions.local.json and the owner turns on in Settings. None is
compiled in or on by default.
Download the new Worker archive. Check its checksum. Extract it into a new directory.
Keep your existing deployment config and secrets. Copy the new worker.js and
migrations/ into your deployment directory. Do not replace your config with the
archive's generic template. Keep main: "worker.js" and no_bundle: true.
Save a D1 backup before any required schema changes. Run these commands from your deployment directory:
npx wrangler@4 d1 migrations apply DB --remote # if the changelog lists migrations
npx wrangler@4 deployDeploying replaces code. It does not reset D1, R2, or stored Cloudflare secrets.
The archive does not include npm run upgrade. Use these steps for each release.
Commit or stash local changes first, including your deployment config, then run:
npm run upgradeThe script downloads upstream tags, merges the newest release, preserves your
wrangler.jsonc on conflicts, installs dependencies, applies new migrations,
and runs type checks and tests. It asks before deploying. git pull tracks a branch.
It does not select the latest release. A GitHub source ZIP has no Git history,
so it cannot use this script. Clone the target release. Copy your config into the new checkout.
For installations from the old blygger-spec/worker/ tree, clone this repository
fresh and copy your account, D1, R2, routes, and vars into its config. Git history
changed during the split. Pulling the old repository will not upgrade Studio.
Keep your fork's CLIENT name if you changed it.
A release is cut by pushing a v{version} tag. Release CI runs the full checks
and then publishes the downloads at that tag. Merges to main do not release.
Before tagging, bump package.json, the root versions in package-lock.json,
and CLIENT.version in src/client.ts. Add a matching changelog entry with a
Migrations: line. Bump sdk/package.json when its public API changes.
Release CI rejects a tag that does not match package.json, or a version that
is not newer than existing release tags.
GitHub Actions must be enabled and allow the release job's contents: write
permission. CI checks types, contract/SDK drift, Worker and browser tests, and
extracted release artifacts. It needs no Cloudflare or npm publishing credentials.
Publishing a release does not deploy anyone's Worker.
Build and check the downloads locally with:
npm run release:check
npm run release:build
npx playwright install chromium
npm run release:verifyRelease verification installs the SDK in a temporary project and tests Node and Chromium. It also checks data after a fresh local Worker process starts.
The commands write downloads to build/release/. release:check requires a new unreleased version.
For an already published checkout, use only the build and verification commands.
src/index.ts routes — studio and /api registered before the public sub-app
src/protocol.ts the wire: manifest, feed, item documents
src/model.ts publish/withdraw/pin/restore, the state machine
src/importer/ subscribe side — resolve, feed parse, poll, hoppers, L0
src/mentions/ Webmention in and out, structural verification
src/tk.ts TK scope grammar · src/tk-generate.ts instructed generation
src/stub.ts stubs (respond) · src/fork.ts forks and lineage
src/pages.ts public server-rendered pages
src/spa.ts Studio shell, login/logout, embedded browser assets
src/ui/ React Studio, Router loaders, DB collections and Base UI
migrations/ D1 schema, 0001–0017
scripts/ export, deploy-all
wrangler.jsonc your deployment — generic here; `npm run init` fills it in
deploy-targets.json your live deployments (gitignored; see the .example)
Those last two are the only files that describe an instance. Nothing under
src/ should ever need editing to configure yours — if it does, that is a bug
in this client, because it would put your changes in the way of every upgrade.
Please report it rather than working around it.
Client version and protocol version are independent, deliberately. This client
is 0.10.0 and implements protocol 0.3. The manifest carries both — blyg is the
protocol version, generator is this client's identity — and per the spec's
decision #18d generator is informative: no reader may gate behaviour on it.
Nodes running the older blyg-ref/0.3.0 build keep working and report
that version string. It is the same software. The name changed when the client
moved out of the spec repo at session 26 (2026-09-28).
People already do, and that is fine. Three requests, all so that the upgrade path keeps working for you:
- Change
CLIENTinsrc/client.ts. A fork that keeps reportingblygger-studio/0.10.0makes the ecosystem census wrong for everyone, and it is the census that drives update notices. Give your fork its own name and version — that is whatBlynger,blyg-publisherand the rest do. - Tell us it exists, so it can be listed at
blygger.organd so a breaking change to an extension point can be announced rather than discovered. Copies outside GitHub forks can be hard to discover. - Number your own migrations from
9000_. Upstream migrations are numbered0001_upwards and will never use9000_–9999_; that range is reserved for local changes (studio#10). D1 records applied migrations by filename, so a local0011_…worked but collided in name with upstream's0011_…, and an upgrade became a careful three-way merge. With local migrations at9000_and up,wrangler d1 migrations applyruns upstream's new ones in order and leaves yours alone. Two consequences to plan for:- Your migrations sort after every upstream one, so a local migration must not depend on an upstream schema change that hasn't shipped yet.
- When upstream adds a column or table you also added locally, the upstream
migration will fail on your copy. Read each release's
Migrations:line before applying, and drop or rename your local equivalent first.
A stable publishing API — so that tools can write to a blyg without changing its
client — is the open design question tracked as item 1.8 in
roadmap-tracks.md.
Studio 0.9.0 replaces older private write routes and request forms.
Third-party authoring tools must adopt the new contract.
See the owner API guide for the replacement routes.
The private Studio API now has a checked-in OpenAPI contract and a
generated JavaScript/TypeScript SDK. The owner can download the same
contract at /api/openapi.json. It uses the existing session cookie. This API does
not define a public publishing protocol.
npm run openapi updates the contract. npm run sdk:generate rebuilds the SDK
with a pinned Hey API generator on Node 22.18+, using the unchanged spec.
Dependency installation builds the SDK and Studio assets, including during upgrades from 0.8.3.
The npm dev, test, and deploy scripts also run npm run build for you.
Build first when you run TypeScript, Vitest, or Wrangler directly.
CI checks types, contract and SDK drift, Worker tests, and Chromium browser tests.
Studio 0.10.0 uses React, TanStack Router, TanStack DB, and Base UI. Route loaders
preload collections through the SDK. Active views poll the D1-backed API every
15 seconds, pause while the tab is hidden, and refresh on focus. Polling keeps
unsaved editor text. The Worker embeds the browser assets. Deployment needs no
extra asset binding. For a mounted installation, forward {mount}/* and
/api/*, including {mount}/studio/app.js and {mount}/studio/app.css.
See the migration plan for the completed SPA cutover and the OAuth and MCP client access.
npx playwright install chromium
npm run test:e2eThe browser suite runs an in-memory Worker, D1 database, and R2 bucket. Outbound requests use a local fixture response. The tests need no deployment config or host credentials.
This repository separated from
blygger/blygger-spec at session 26
(2026-09-28). git subtree split preserved all 59 commits from worker/.
The spec repository now contains only normative protocol material.
The split separates protocol issues from bugs in this client. A report such as "transclusion is broken" could refer to either. Six other implementations also use the protocol.
MIT. The protocol documents live in blygger-spec under CC-BY-4.0.