WordPress Publishing for RailCall
WordPress plugin and RailCall module for approval-based publishing, reviewed changes, stale-review protection, scheduling, and audit receipts.
by Niko Codes · github.com/lame13/wordpress-publishing-for-railcall · website
Install
No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:
wp plugin install https://github.com/lame13/wordpress-publishing-for-railcall/archive/refs/heads/main.zipWebsite · Download v1.0.0 · Report an issue
Governed publishing for WordPress. An agency prepares a change, a human sees the exact diff on the exact post, approves it, and the site applies it — then the public page is checked for reachability and a content sample.
This repository ships the pair:
| Piece | Where | What it does |
|---|---|---|
| WordPress plugin | repository root | Owns review tokens, single-use approvals, the activity log and the REST surface (railcall/v1) on the site. |
| RailCall module | railcall-module/ |
Ten commands the RailCall station calls: read, preview, approve, publish, schedule, verify. |
Who it is for
Content agencies and site teams whose writers or clients must approve a change before it is public — and who then need evidence of what went live. RailCall prepares; a human approves; the site refuses anything else.
Install
- Download
wordpress-publishing-for-railcall-1.0.0.zipfrom the release. In WordPress, open Plugins → Add New Plugin → Upload Plugin, upload the ZIP and activate WordPress Publishing for RailCall. Requires WordPress 6.5+ and PHP 7.4+. - Create an Application Password for the account RailCall should use (Users → Profile → Application Passwords). That account needs edit and publish rights; an Editor account is enough.
- Open RailCall publishing → Overview. It says whether the site can publish right now; entries in the activity log are listed separately as history, because a failure from earlier does not stop the next approval.
- Download
railcall-wordpress-publishing-1.0.0.zipfrom the same release and extract itswordpress-publishingfolder into~/.railcall/station/modules/. The bundle is signed; preserve its files together. Startrailcall studio, open Modules → Reload all, and confirm all ten commands load. If Station rejects the publisher, follow its trust policy rather than disabling signature checks. - In Studio → Integrations, configure the
wordpressconnection:
| Field | Example |
|---|---|
WP_SITE_URL |
https://example.com |
WP_USERNAME |
railcall |
WP_APPLICATION_PASSWORD |
abcd efgh ijkl mnop qrst uvwx |
The release includes the plugin ZIP, the signed module ZIP and SHA256SUMS.txt.
The GitHub source archive contains the code and tests. This project is published
on GitHub; it is not a RailCall marketplace listing.
The module reads credentials from RailCall's vault instead of environment variables or bundled configuration. It does not include them in receipts.
On Station 1.5.34, numeric inputs use its number field type and boolean
options use the strings "true" and "false". The handler validates post IDs
and converts those options before calling WordPress. Station requires approval
for credential-backed read commands too unless the operator allows those reads.
One working example
Review a draft title, then publish it:
wordpress.list_posts → status: draft → post_id 41
wordpress.preview_change → post_id 41, title "Autumn launch (v2)"
→ changes: 1 field (Title), review token rc_…
…operator approves in RailCall…
wordpress.update_draft → post_id 41, review_token rc_… , title as approved
→ changed: true, state_after 9c1f… (was 4ab2…)
wordpress.preview_change → post_id 41, action publish
wordpress.publish_post → changed: true
→ evidence.public_page: http 200, content found true
wordpress.verify_publication→ verdict: reachable_content_sample_found,
https://example.com/autumn-launch/
Edit that draft in WordPress between the preview and the approval and
wordpress.update_draft is refused with rcpub_review_stale and a re-review
instruction. Nothing is written.
What each write command does — and does not do
| Command | Applies to | What the approval covers |
|---|---|---|
wordpress.create_draft |
new posts | nothing public: a draft is not visible |
wordpress.update_draft |
drafts, pending and private posts only | the reviewed fields, on a post that is not live |
wordpress.publish_post |
drafts and already-live posts | the reviewed fields and making them public, in one approval |
wordpress.schedule_post |
drafts and scheduled posts | the reviewed fields and the exact UTC time they go live |
update_draft refuses a published or scheduled post, because its confirmation
reads "nothing public changed until it is published" and that would be untrue.
Changing what a live page shows is publish_post, where the diff the operator
approved is the diff the site applies. Publishing a draft twice is safe: the
same approval replays the first result, while a new approval for the same post
is a new operation with its own idempotency key.
What the publication verdict means
| Verdict | What was actually verified |
|---|---|
reachable_content_sample_found |
The public URL returned 200, shows the post title, and contains a sample of the stored content. Not proof that the approved version is visible: the sample is taken from the opening text, so an older cached page that shares it passes too. version_confirmed is false on every result. |
reachable |
The URL returned 200 and shows the post title, but the stored content is not on the page. A page exists; an old cached copy may be what answered. |
not_public / scheduled |
The post is a draft, private, or waiting for its scheduled time. |
live_unverified |
The post is published but the page could not be fetched at all. |
The verdicts are evidence, not a guarantee. Report the URL and these findings, and open the page when the exact rendered version matters.
Known limitations
- The plugin must be active on the site; the module deliberately does not write through core WordPress endpoints, so the review state lives on the site.
- Application Passwords require HTTPS (or a local environment).
- Station 1.5.34's Reject button returned 404 during integration testing. Backend denial was verified through Station's POST endpoint. A successful click on that UI button has not been demonstrated; see REVIEW.md.
- WordPress publishes a scheduled post on the next request after its time (WP-Cron), so a quiet site can be a few minutes late.
- HTML is filtered with
wp_kses_postunless the account may post unfiltered HTML; the preview says which filter will run. - An empty string clears content or an excerpt, and an empty tag list removes every tag. An empty category list is refused: WordPress never leaves a post without a category, so it would silently reassign the site's default instead.
- Categories and tags are resolved before the post is written, so a term this site does not have stops the request with the post unchanged. Creating new terms also needs the site-level setting to allow it.
- Multisite: activate per site. Uninstalling removes the plugin's own tables and options and never touches posts.
- The RailCall module declares no egress sandbox block, because the site host is
per-install. It only ever calls the single host in its vault credentials, and
it says so in
railcall-module/handlers/handler.py.
See TESTING.md for the reproducible test run (including
tests/demo-sequence.py, which rehearses the plugin's review-token path and
prints a transcript you can film — it is not a substitute for the Station run,
which is where approval, denial and the signed receipt live),
CHANGELOG.md for history, and readme.txt for the
WordPress-style readme.
See REVIEW.md for the September 21 review, verified Station receipts, and integration limitations. Those receipts describe the development build tested on that date, not a marketplace certification of v1.0.0.
License
GPL-2.0-or-later.