Interop is the release bar (see
spec/conformance-and-testing.md). This
directory holds the machine-readable status that turns "conformance" from a
nice-to-have into an enforced gate.
The suites run against packages/conformance-target — a private Worker
composing every endpoint package, deployed to https://conformance.dwk.io via
the deploy-target job in .github/workflows/conformance.yml. See
packages/conformance-target/README.md for setup and per-suite runbooks.
A single source of truth for every workspace package:
status is one of pending, failing, passing, not-applicable.
integration describes a deployed lifecycle — a package brought up behind
its bindings and exercised over the wire. The cross-standard reusable libs that
take plain-data inputs and declare no bindings have no such lifecycle to run:
their colocated unit tests are the whole story, and leaving them pending
would block a stable release on a run that can never happen. Those packages
carry integration.status = "not-applicable":
@dwk/dpop, @dwk/rdf, @dwk/wac, @dwk/log, @dwk/ldn,
@dwk/http-signatures, @dwk/oauth, @dwk/calendar, @dwk/safe-fetch,
@dwk/esi, @dwk/mf2.
This exempts only the integration lifecycle. A lib that has a conformance
suite still has to pass it (@dwk/ldn's ldn-test-suite, for one), and every
endpoint package — plus the binding-bound libs (@dwk/store, @dwk/cf-shims,
@dwk/deno-host) — stays gated on a real run.
It is also not on its own enough to let one of these libs go stable. The
gate refuses a stable package whose suites are empty and whose integration
is not-applicable, because that combination would pass vacuously — zero
recorded evidence:
@dwk/dpop@1.0.0: stable package with no conformance suites and integration
"not-applicable" — mark at least one suite as "not-applicable" to gate the release.
So stabilising @dwk/dpop, @dwk/rdf, @dwk/wac, @dwk/log, @dwk/oauth,
@dwk/http-signatures, @dwk/safe-fetch, @dwk/esi, @dwk/calendar, or
@dwk/mf2 still needs a deliberate per-package call on the suites side —
naming an external suite and running it (RFC test vectors for
@dwk/http-signatures, the microformats2 parser tests for @dwk/mf2, an
iCalendar validator for @dwk/calendar), or recording an explicit
not-applicable suite entry saying no external suite exists for it. That is a
judgment call per package, deliberately left to the release owner rather than
defaulted here.
Conformance is tracked per target, declared at the top of status.json:
cloudflare— Cloudflare Workers, the primary, recommended, conformance-certified target. The flatstatuson each suite/integration is this target.node— the self-hosted Node/Express host (the@dwk/serverDocker image ordwk-servebin). Recorded in the optionaltargets.nodeslot on a suite or integration block, plus the@dwk/serverpackage's own row.
The Node host's integration lifecycle is already green for every package it
brings up end to end (the @dwk/server phase2–phase5 tests), so those carry
integration.targets.node = "passing". The hosted suites (micropub.rocks,
etc.) against a deployed Node host stay pending until a public @dwk/server
target is wired in — record them with
run-suite.mjs <standard> --target <url> --target-id node. The gate checks every
target's status, so a stable package must be green on each target it declares.
fedify is a third kind of entry, specific to
@dwk/activitypub.suites.activitypub-federation.targets: unlike cloudflare/
node (which deployment serves the package under test), fedify names the
interop peer the suite was run against — a scripted
Fedify actor
(scripts/conformance/fedify-peer.mjs), a second, automatable federation
implementation alongside manually-tested Mastodon (node) (issue #246).
scripts/release-gate.mjs (run via pnpm release:gate) reads each package's
version and this file. A package is gated once its version is stable
(major >= 1, no prerelease tag). A gated package whose suites or integration
status is not passing/not-applicable blocks the release:
- It runs inside
pnpm release, beforechangeset publish. - It runs in CI on every PR/push (
.github/workflows/conformance.yml). pnpm release:gate -- --reportprints the status table without enforcing.
Because every package is still on a 0.1.0-beta.N prerelease, nothing is gated
yet — but the wiring is live, so the first attempt to bump a package to 1.0.0
without recording its conformance green will fail.
| Package | Standard | Suite(s) |
|---|---|---|
@dwk/micropub |
Micropub | micropub.rocks |
@dwk/webmention |
Webmention | webmention.rocks (recv + send) |
@dwk/solid-pod |
Solid | Solid conformance test harness + real-client interop |
@dwk/webdav |
WebDAV | litmus (basic, copymove, props, locks) |
@dwk/activitypub |
ActivityPub | Mastodon (manual, target node) + Fedify interop peer (target fedify) + Pixelfed/Lemmy (manual, targets pixelfed/lemmy, fediverse interop #273) |
@dwk/indieauth |
IndieAuth | integration + interop (no hosted "rocks" suite) |
| libraries | — | unit/integration only |
The hosted suites exercise a deployed, publicly reachable Worker, so they cannot run against in-process Miniflare. Point them at a deployed target:
node scripts/conformance/run-suite.mjs micropub --target https://example.com/micropubWithout --target the dispatcher prints the procedure and exits 0 (a documented
no-op, so ordinary CI stays green). After a suite passes, record the result —
including the published implementation-report URL for Micropub — by setting the
relevant entry in status.json to passing. The next release gate run will
then allow that package to go stable.
Micropub and Webmention are manual, hosted-web-app suites — see
micropub-qa.md and webmention-qa.md
for the fillable, step-by-step QA runbooks (exact endpoints, a results
checklist, and troubleshooting). Both are meant to be re-run before every
release that touches the respective package, not just once.
WebDAV/litmus is executable, not just documented. litmus is a real CLI, so the dispatcher actually runs it when given a target and Basic credentials (an app password minted via the owner-gated endpoint), exiting with litmus's status:
WEBDAV_USERNAME=… WEBDAV_PASSWORD=… \
node scripts/conformance/run-suite.mjs webdav --target https://pod.exampleThe hosted-suite workflow installs litmus and supplies the credentials from the
WEBDAV_USERNAME / WEBDAV_PASSWORD repo secrets on manual dispatch or the
weekly schedule. See webdav-qa.md for the fillable,
step-by-step QA runbook (credential minting, both invocation paths, a
results checklist, and troubleshooting).
ActivityPub/Fedify is executable too. The Fedify peer needs a public URL to
receive callbacks for every case but the read-only webfinger one. The
hosted-suite workflow auto-provisions a Cloudflare Quick Tunnel on the
runner (override by passing peer_url at dispatch); for a local run start
one yourself (cloudflared tunnel --url http://localhost:8765):
node scripts/conformance/run-suite.mjs activitypub \
--target https://example.com/actor --peer-url https://<tunnel>.trycloudflare.comscripts/conformance/fedify-peer.mjs can also be run standalone (see its own
usage comment) for
--case webfinger,follow,activities,page,announce-unwrap,fanout,rsvp;
rsvp is reported skipped, not silently dropped, without --event, and
fanout + announce-unwrap (the FEP-1b12 group-relay lifecycle: the target
follows the peer's Group, receives the Accept, then accepts the group's
Announce(Create(Page))) are likewise skipped without the owner publish
channel — --publish-url (the target's POST <actor>/outbox) and
--publish-token (or FEDIFY_PUBLISH_TOKEN; the workflow derives the URL
from target_url and reads the token from the FEDIFY_PUBLISH_TOKEN repo
secret, which mirrors the target's ACTIVITYPUB_PUBLISH_TOKEN deploy
secret). Record activitypub-federation -> targets -> fedify as
passing once every non-skipped case passes.
Fediverse interop #273 phase 1 acceptance, against any live Pixelfed instance
(e.g. an account on pixelfed.social). See
pixelfed-qa.md for the fillable, step-by-step QA runbook
(exact commands, a results checklist, and troubleshooting) — the summary below
is the quick reference:
- From the Pixelfed account, search the actor's handle
(
@user@your.domain) and follow it; confirm the follow is accepted (the follower appears in the actor'sfollowerscollection). - Publish a media note through the shaped-publish endpoint:
POST <actor>/publishwith{"kind": "note", "content": "…", "sensitive": true, "attachments": [{"type": "Image", "url": "…", "mediaType": "image/jpeg", "name": "alt text"}]}(bearerpublishToken). - Verify in the Pixelfed timeline: the post renders with its image
(text-only posts never appear in Pixelfed), the alt text is preserved on
the media, and the content warning (
summary/sensitive) conceals it. - Like and reply from Pixelfed; confirm both land in the actor's inbox
(
activitypub_list_inboxor the DO's inbox table). - Record
activitypub-federation->targets->pixelfedaspassingwith the run date.
Fediverse interop #273 phase 2/3 acceptance, against any live Lemmy instance.
See lemmy-qa.md for the fillable, step-by-step QA runbook
(exact commands, a results checklist, and troubleshooting) — the summary
below is the quick reference:
- Follow a community by handle: publish
{"type": "Follow", "object": "https://<instance>/c/<community>"}viaPOST <actor>/outbox, or use theactivitypub_resolveMCP tool to resolve!community@instancefirst. Confirm thefollowingrow reachesacceptedwithactor_type = 'Group'. - Wait for community activity (or post something there from another
account): confirm announced posts are unwrapped — inbox rows for the
inner activities carrying
relayed_by= the community IRI — and that theirverify_stateadvances toverified. - Post into the community:
POST <actor>/publishwith{"kind": "page", "name": "Title", "content": "…", "audience": "!community@instance"}; confirm it appears as a post in the community (title intact) once the community announces it. - Reply to a community post (
note+inReplyTo+ the communityaudience) and vote —POST <actor>/outboxwith{"type": "Like"/ "Dislike", "object": "<post-iri>", "audience": "<community-iri>"}(theaudienceis required: a vote'sobjectnames content, not an actor, so there is no inbox to derive delivery from without it — omitting it means the vote never reaches the community at all); confirm both register on Lemmy. - Record
activitypub-federation->targets->lemmyaspassingwith the run date.
Central mode (spec/scale-out.md) is packaging/tooling-complete but
experimental, not supported (host-contract §9) until its own
live-verification checklist and hosted-suite run pass against a real ≥2-replica
deployment — the in-memory fakes the colocated @dwk/server tests use can't
prove real-service behavior (hrana read-your-writes, sqld under concurrent
writers, MinIO read-after-write, …). See
scale-out-qa.md for the fillable runbook, run against
packages/server/docker-compose.yml
(sqld + MinIO + 2 replicas + nginx) — that compose file doubles as the test
bed and the central-mode deployment example. Re-run it before every release
that touches central mode's mechanism (central-bindings.ts,
central-mode.ts, central-durable-object.ts, central-do-poller.ts,
libsql-kv.ts, or @dwk/deno-host's D1/R2/lease/alarm/queue modules).
The four verb-lifecycle cases the spec requires live as colocated integration
tests in packages/solid-pod/src/index.test.ts and run under workerd via
@cloudflare/vitest-pool-workers (pnpm test:integration):
- authenticated
GETrouted through WAC, PATCHwith asolid:wherematch,PATCHwhosewheredoes not bind (expect 409),If-Match/ ETag preconditioned writes.