Skip to main content
You can connect a broken integration. Castro will happily complete the handshake, publish a post, and report success, while your PUT /posts/{id} quietly wipes the title off every post it touches. This page gives you a conformance script that finds those bugs in about ten seconds. Run it before you connect, and again after any change to your endpoints.
The script is a single file with zero dependencies. It needs Node 18+ (for built-in fetch) and nothing else. Nothing is installed, nothing phones home.

Run it

1

Save the script

Copy the code below into a file called castro-conformance.mjs.
2

Point it at your endpoints

Run it against a staging environment first: it creates and deletes real content.
3

Fix what fails

Every failure prints what was expected and what came back. The troubleshooting guide explains each one.

What it checks, and why each one matters

The script only tests things that actually break in production. It reads your declared capabilities from the handshake and skips whatever you didn’t implement.
Your challenge_response must equal HMAC-SHA256(challenge, your_key).The subtle failure here: signing the challenge with the key that arrived in the request header instead of your own stored key. That returns a valid response to anybody who asks, which proves nothing at all. The script catches it by sending a challenge and checking the answer against the key you passed on the command line.
This is the one that costs you real content.Castro’s Update Content action sends only { content, source_id }. If you treat PUT /posts/{id} as a full replace, every such update erases the title, categories, author and SEO the user carefully set.The script publishes a post with all of those fields, sends a body-only update, then reads the post back and asserts the title survived.
It’s easy to write signature verification that never says no: a try/catch that swallows the mismatch, or a comparison against an undefined variable.The script sends a deliberately wrong signature and requires a 401. If your server returns 200, your endpoints are open to the internet.
The signature covers the exact bytes Castro sent. If you parse the JSON and re-stringify it to verify, key order and whitespace shift, and the signature will never match, but only for some payloads, which makes it look intermittent.The script sends a body whose key order changes under a naive re-serialize, so a raw-body bug fails here instead of in production.
Castro signs with Date.now(): unix milliseconds. Reject anything more than a few minutes old.The script sends a seconds-based timestamp and requires a 401. If you accept it, your replay window is roughly 50,000 years wide.
GET /pages is defined as listing published pages. Castro treats everything you return as live and publicly reachable: a draft in that list gets queued for crawling and shows up as a real page.

The script

castro-conformance.mjs

A clean run

A failure names the fix, not just the symptom:
All green? You’re ready to connect. Work through the checklist for the handful of things a script can’t assert.

Testing without touching your backend

Not ready to point this at real code? Run the reference receiver, a complete, correct implementation of the entire contract, and run the script against that first, so you know what a passing run looks like:
The script publishes and deletes real content. Point it at staging, or at a throwaway environment, never at a production site with live traffic.