Releases: patl4588/railcall-core
Release list
Station v0.27 — headless CLI auth (API keys in the terminal)
RailCall Station v0.27 — headless CLI auth (API keys in the terminal)
Status: cut. Small, focused release on top of v0.26.
v0.26 shipped the marketplace-side API-key surface (create/list/revoke +
BearerAuthGuard on listings.publish). This release closes the loop
by making those keys usable from the CLI without opening a browser —
the actual CI/CD story customers care about.
What's new (CLI-side)
1. railcall market api-keys subcommand. Three verbs, all direct
mappings of the web-UI surface at /marketplace/settings/api-keys:
railcall market api-keys list
railcall market api-keys create <name> [scopes]
railcall market api-keys revoke <id>
Fresh secret prints ONCE to stdout on create (matches the web UI's
one-shot banner). Standard panel() output; scriptable if a caller
wants to grep the rc_ak_live_… line out of stdout.
2. RAILCALL_API_KEY environment variable. Every marketplace-authed
command now checks the env var first and falls back to the interactive
session (railcall market login) only when it's unset. Precedence is
deliberate: if the key is set but rejected, we DON'T silently downgrade
to the session — that would mask credential misconfigs in CI/CD (worst
class of debugging).
Typical CI/CD flow:
export RAILCALL_API_KEY=$(op read op://vault/railcall/api_key) # 1Password
railcall market publish path/to/module.json # signed publish
Zero shell state. No cookies, no session files, no browser.
3. api-keys management endpoints refuse API-key auth. Deliberate
guard against the lateral privilege loop: an API key can't mint or
revoke other API keys. If the CLI detects RAILCALL_API_KEY is set
during an api-keys subcommand, it prints an actionable message
pointing the user at railcall market login OR the web UI, instead of
firing the request and returning a confusing 401.
Also in v0.27 (marketplace + website side, deployed already)
Everything in the "Post-cut additions" section of the v0.26 release
notes remains live and untouched. Nothing on the marketplace side
requires a station re-download to work — the CLI just talks to the
production API. Recap:
- Session invalidation on member removal / role demotion
- Admin audit log (
OrgAuditLog+/org/audit+/marketplace/org/auditviewer) - Vault admin UI + SCIM group-map UI
/trust/subprocessorspage + SOC 2 statusPOST /auth/api-keys+BearerAuthGuardwired intolistings.publishGET /org/export(GDPR Art. 20)/statuslive-probe page
Verify
sha256_of_railcall_station.tar.gz = c519d6b1085fb8e20d9c100baf85754200363d7dc34d46a79105ad5c9d8afa2d
sha256_of_railcall_cli.py = 1ea60913e9ba5a9ceb8798aa01ac33ac01e3b80eeea2c998a596333070536f1f
sha256_of_railcall_vault_drivers.py = 7ff6896532adcbcf8302039bafe403884f457d8fb2ed87320e8283c61f9df096
Every other file pin (governance package, signer, companion daemon,
vault_io) is unchanged since v0.26 — cross-verified before the cut.
Deferred to v0.28+
railcall audit view— CLI wrapper aroundGET /org/auditso
admins can grep the audit log from the terminal.railcall org export— CLI wrapper aroundGET /org/export.- Studio session picking up the org identity from the JWT / API key
(today the Studio side reads~/.railcall/marketplace_session.json
directly; API-key-only setups need a small refactor).
Station v0.26 — enterprise readiness (SSO + RBAC + SCIM + configurable vault)
RailCall Station v0.26 — enterprise readiness (SSO + RBAC + SCIM + configurable receipt vault)
Status: cut. Tarball built, install.sh pinned, GitHub release published.
The most significant single release since v0.22 (marketplace/modules launch). v0.25 was the last "solo-user" cut; v0.26 is the first release where a team-sized customer can honestly buy Enterprise:
- Log in with their identity provider (WorkOS SSO — Okta, Azure AD, Google Workspace, Ping, any SAML/OIDC).
- Auto-provision + auto-remove users when their HR system flips them (SCIM Directory Sync).
- Assign roles across their team (owner/admin/publisher/operator/viewer).
- Publish modules only their team sees (internal-only marketplace visibility).
- Stream every signed receipt to a shared vault they control (local path, external SSD, NAS mount, USB stick, or an S3-compatible bucket — one config, every seat writes there).
Every existing solo install keeps working unchanged. The vault config is optional, additive, and best-effort — a mirror on top of the local write path that has always been the trust surface. If the vault is unreachable, the local write still happens; the run never fails on a mirror.
What's new
1. WorkOS SSO — /marketplace/sso on the storefront + /auth/sso/* on the marketplace API. Enterprise buyer types their work-email domain; browser bounces through their IdP; JIT-provisions User + Membership; mints the same JWT+refresh pair the password flow issues. First user on a fresh WorkOS org auto-lands as owner because they're almost certainly the identity admin who set up SSO in the first place. Degrades cleanly without WORKOS_API_KEY — the SSO routes 503 with an actionable message; every other endpoint keeps working.
2. Multi-seat + RBAC — Organization + Membership tables with a five-role ladder (viewer < operator < publisher < admin < owner). Full invitation flow: POST /org/invitations (admin+), invite email with a sha256-hashed token, POST /org/invitations/accept. Last-owner protection prevents the org from becoming ownerless via demote/remove. Frontend: /marketplace/org/members (roster + invite + role dropdown + revoke).
3. SCIM Directory Sync — POST /webhooks/workos signature-verified receiver. Handles dsync.user.created/updated/deleted + dsync.group.user_added/removed. When the customer's IdP flips a user, we mirror it: JIT-create Membership, sync attributes, mark Membership.removed_at on deletion. Group→role mapping via Organization.dir_group_role_map (RailCall staff sets it after the customer creates their IdP groups). Group elevation is additive-only — a manual UI promotion isn't clobbered by an unrelated group event.
4. Internal-only marketplace — ListingVisibility enum (public | org_internal) + Listing.owner_org_id. Publisher-role members can publish listings that only members of their org can see + install + purchase. Non-members get 404 (not 403) on both browse and detail so slug enumeration returns nothing.
5. Configurable receipt vault — Organization.vault_config JSONB + admin surface. Every Studio in the org fetches the config on boot (5-min TTL cache) and mirrors every receipt + audit line to whatever driver the org set. Drivers:
local— filesystem path. Covers external SSD, NAS mount, USB stick — the OS mounts, Studio writes.s3— hand-rolled AWS SigV4 over urllib. Zero deps. Works with AWS S3, MinIO, Cloudflare R2, GCS interop, or any S3-compatible target viaendpoint_url. Verified end-to-end against MinIO.network_share— SMB/NFS wrapper. OS mounts; Studio checks mount presence + writes.custom— importlib loader. Customer ships their own driver in~/.railcall/vault_drivers/; config{driver:"custom", module_ref:"pkg.mod:Cls"}loads it at Studio boot.
Fail-mode is fixed: always fall back to local. Local write happens first (unchanged from v0.25). Vault mirror runs after in a swallowed try/except with stderr logs. Vault failure never breaks a governed run.
Secrets never in the marketplace DB. S3 access keys and custom-driver secrets are configured as references (env:VAR, file:/path, keyring:label) — the Studio-side driver resolves them at runtime. The marketplace explicitly rejects raw *_key/*_secret at the boundary with an actionable error.
Also in v0.26
- Air-gap install kit (Enterprise plan Week 1) —
railcall_station_v0.26_airgap.tar.gzfor customers who cannot let outbound traffic reach railcall.ai. Every file listed inMANIFEST.txtwith its sha256. Full offline install path. - Login unification — the single
/signinon railcall.ai now routes to the marketplace login; the CLI's gateway flow lives at/cli-activate. One login story per user. - HIPAA trust page —
/trust/hipaadocuments BAA scope, technical controls, receipt-vault architecture. Buyable-tier compliance leave-behind.
Post-cut additions (2026-07-25 same day)
The initial v0.26 cut was extended after Sami's audit turned up
enterprise gaps a CISO would name in a procurement review. All of
these landed the same day, on top of the tagged station tarball —
they're marketplace + storefront side, so they roll out via the
regular deploy chain, not the versioned release.
Session invalidation: User.session_generation bumps on member
removal + role demotion. AuthService.invalidateAllSessions revokes
every refresh token for the target user in the same transaction. Max
window from "you're fired" to "your JWT stops working" = 15 min
(access-TTL grace).
Admin audit log: OrgAuditLog append-only table + write hooks
on every admin mutation surface (invite / role change / member
removal / vault config / SCIM events / org export). Frontend at
/marketplace/org/audit with event-slug filter + keyset pagination.
Vault admin UI: /marketplace/org/vault-config — driver picker
with per-driver form fields. Refuses raw secrets at the input the
same way the API does; only env: / file: / keyring: references
accepted.
SCIM group-map UI: /marketplace/org/dir-role-map — add / edit /
remove (WorkOS group id → OrgRole) rows. Self-serve for the
customer's own admin (the staff endpoint at /admin/orgs/:id/dir-role-map
still exists for support-team bootstrap).
API keys for CI/CD: /marketplace/settings/api-keys — long-lived
tokens with rc_ak_live_ prefix, sha256-hashed in the DB, per-key
scopes, optional expiration, best-effort last_used_at. Wired into
listings.publish (headless CI can publish modules); other endpoints
can adopt BearerAuthGuard when needed.
Data export: GET /org/export — GDPR Art. 20 / CCPA "right to
access" dump of everything the marketplace stores about the org.
Owner-only. Recorded in the audit log itself.
Subprocessor list: /trust/subprocessors — public per-vendor
cards (Render / WorkOS / Stripe / Resend / GitHub / Cloudflare) with
data categories, jurisdiction, notes. Includes vendor-management
policy summary + 30-day new-vendor notification commitment.
Status page: /status — live client-side probes of railcall.ai
- marketplace API, auto-refresh every 30s.
Verify
sha256_of_railcall_station.tar.gz = e90027bf8c28057fcb461ebda9dff7a577918ee11e764461e6c4c4f2585e6626
sha256_of_railcall_cli.py = 9a607b4cb41987685573b1624a85f70264819e3a537b43ded3013364604bbc95
sha256_of_railcall_vault_drivers.py = 7ff6896532adcbcf8302039bafe403884f457d8fb2ed87320e8283c61f9df096
Every install.sh downloads is pinned above; a wire tamper fails the pin gate before anything reaches disk.
Deferred to v0.27+
railcall_hostedvault driver — requires a marketplace-side vault-ingest endpoint + storage decision. Separate design conversation.- Weeks 13-16 of the Enterprise plan (SLA infra, BAA workflow, policy engine).
- Studio session picking up the org identity from JWT claims (marketplace org membership → Studio session context).
station-v0.25 · publisher trust allowlist (Phase 4a sandbox)
What's new
Publisher trust allowlist (Phase 4a of the module sandbox)
Signature verification proves WHO signed a module bundle. This release adds
the second question: WHICH publishers are allowed to register commands on
THIS install. Two modes:
trust_mode: any(default) — historical behavior; any valid
signature accepted. Existing installs unchanged.trust_mode: allowlist— strict; only publishers in
<ws>/publisher_trust.jsonregister commands. Rejections surface the
publisher fingerprint + a copy-pasteablerailcall trust addhint in the
Modules tab.
First-party publishers (Sami's marketplace publisher key 726b327ec6…)
ship pre-trusted in the default allowlist, so first-party modules keep
working when a user flips to allowlist mode.
New CLI: railcall trust
railcall trust list mode + allowlist snapshot
railcall trust add <pubkey> [--name] trust a publisher
railcall trust remove <pubkey> untrust a publisher
railcall trust mode any|allowlist flip enforcement mode
New endpoints
GET /api/trust/state— mode + allowlistPOST /api/trust/add— {pubkey, publisher_name?, note?}POST /api/trust/remove— {pubkey}POST /api/trust/mode— {mode: 'any'|'allowlist'}
All session-gated; auto-trigger a modules reload so newly-trusted (or
newly-untrusted) modules register/unregister live.
Sandbox roadmap
- 4a (this release): trust allowlist — WHO's allowed
- 4b (later): subprocess isolation — HOW the still-allowed runs are
contained (per-module network allowlist, restricted stdlib, timeout,
memory limit). Defensive against "trusted publisher pushes bad update";
4a is defensive against "random module from the marketplace runs by
default".
SHA256
44182d0931e8400f0789152b022484522fd3e9f08174a528b6c3f397f0262b91
Install / update
curl -fsSL https://railcall.ai/install.sh | bash # fresh
railcall update # existing
station-v0.24 · Licenses tab + paid-module UX loop + Stripe fixes
What's new
Studio surfaces
- Licenses tab (new sidebar entry) — visual home for module licenses. Cards per license with tier + days-left + grace-warning badges. Activate box (paste JSON or upload file) + per-license Deactivate. Empty state points at the marketplace.
- Modules tab — license-gated rejections now render Buy license (deep-link to marketplace) + Activate license (jumps to Licenses tab) buttons. Other rejection reasons (bad signature, missing files) unchanged.
- Marketplace tab — installing a paid module fires a confirm dialog explaining the buy-then-claim ceremony BEFORE the download, so users don't strand themselves at a rejected-module wall. Success card includes a purple "License required" callout with marketplace deep-link + exact
railcall market claimcommand.
Loader
_load_modulesincludesmanifest.idin license-gated rejection payloads so the frontend can build marketplace URLs without a second lookup.
Marketplace (server-side, already deployed)
- Stripe checkout enforces the $0.50 minimum with actionable error messages (e.g. "listing priced at $0.20 is below Stripe's $0.50 minimum"). Free listings unaffected.
- Stripe's actual error messages now surface to buyers ("card declined", currency mismatch, etc.) instead of the generic "try again".
CLI (unchanged from v0.23 — no re-pin needed)
Still ships license activate/list/verify/deactivate, market claim, connect salesforce.
SHA256
7983ebc49343b3b93ac791bee961ccb634f61da46e3b97866fe94453395cf5c3
Install / update
curl -fsSL https://railcall.ai/install.sh | bash # fresh
railcall update # existing
station-v0.23 · Local DRM + Salesforce module (Phase 2 goes real)
What's new
Local DRM — sellable modules (the Phase 2 unlock)
module_entitlement.pyprimitive — signed per-module licenses, verified OFFLINE against a pinned issuer key. Twin ofentitlement.py(org-tier) but scoped to one module — blast-radius separation between key compromises.- Loader gate — modules with
license_required: truein their manifest refuse to register commands unless a valid, install-bound, unexpired-or-in-grace license file exists underWS/module_licenses/<safe_id>.json. - 7-day grace past expiry — warns but never blocks. Enterprise ops teams must never be gated by billing lag.
- Install binding — every license carries this install's Ed25519 pubkey inside the signed body. Copy a license to a second machine → refused. Nothing to unlock on the box; the lock IS the machine identity.
railcall licenseCLI —activate <path>/list/verify [module_id]/deactivate <module_id>. Delegates to the station primitive (the ONE audited verify path).- Modules tab license badge — tier + days-left when licensed, GRACE warning in the 7-day window past expiry.
First real paid module: railcall/salesforce ($199/mo)
- Installable from marketplace:
railcall market install sami666/salesforce - 13 airlock commands covering the full lead-to-cash surface:
- Leads:
sf.lead.create,sf.lead.update,sf.lead.convert - Contacts:
sf.contact.create,sf.contact.update - Accounts:
sf.account.create,sf.account.update - Opportunities:
sf.opportunity.create,sf.opportunity.update_stage,sf.opportunity.close - Cases:
sf.case.create,sf.case.update,sf.case.close
- Leads:
- OAuth 2.0 refresh-token flow — bring your own Connected App; token never appears in receipts. Sandbox orgs supported via
test.salesforce.comtoken_url. - 14-day free trial available on request.
Also in this cut
- New loader helpers:
_oauth_refresh(provider),_http_get/patch/delete_json— unlocks Salesforce, HubSpot OAuth, Google, Microsoft, and any REST API ongrant_type=refresh_token. _vault_save(provider, value)for helpers that need to persist derived state (e.g. OAuth cached access_token) without going through the credential-save airlock.
SHA256
5089c33f7f051d9390ffaa699cac1d01656aece9ffee36a8cf36e811e5e2f68d
Install
curl -fsSL https://railcall.ai/install.sh | bash
Existing installs
railcall update
station-v0.22 · modules system (Modules tab + HubSpot module + hot-reload)
What's new
Modules system (major)
- Studio Modules sidebar tab — new management surface at
#/modules. Cards for every loaded module show publisher fingerprint, version, description, registered commands as chips, file path. Per-module Uninstall; global Reload All. Rejected modules render in a red section with the loader's exact rejection reason. - Hot-reload without restart —
/api/modules/reloadre-verifies signatures and re-registers commands live.railcall market install <module>no longer needs a Studio restart. - Sidebar badge — Modules tab shows installed-module count.
First real CRM module: railcall/hubspot
- Installable from marketplace:
railcall market install sami666/hubspot - Two commands:
hubspot.create_contact+hubspot.log_note - Auth: HubSpot Private App bearer token (one vault entry, both commands)
- HubSpot v3 CRM API + association type 202 for note↔contact linkage
- Reference implementation for third-party CRM modules
Marketplace fixes
- Publish DTO now accepts
listing_type=module(previously the DTO gate blocked at 400 before payload validation) - Two new categories:
CRMandModules - Reviews eligibility API (
/listings/:slug/reviews/eligibility) — free installs (amount=0) still unlock reviews - Ratings on public seller profile: per-listing + overall
Listing.idrefactored to opaque cuid; human-readableListing.slugnow owns the URL path
Also in this cut
- Marketplace catch-all route
[...slug]on railcall.ai so namespaced slugs (sami666/hubspot) route without URL-encoding - Marketplace browse in Studio: creator/curated/modules sections with trust badges
- Programs → Workflows rename across UI
- Publish button in Workflows with preflight (missing seller profile / unregistered pubkey / no session all surfaced before submit)
- Reviews section on listing detail page
- Ratings-aware seller profile page
SHA256
15ef73f16071c2f543ea9d43fe7bfa354d4bb07dbb860bff231e42cd18659286
Install
curl -fsSL https://railcall.ai/install.sh | bash
Existing installs
railcall update
station-v0.20 · marketplace→Studio + rate limits + allowlists + workflow_compose MCP + Publish button
Major push: creator economy loop closes end-to-end, airlock hardens with rate limits + allowlists, MCP surface adds workflow composition, Studio gains one-click marketplace publish.
Ships:
Creator loop
railcall market install <id>writes a Studio-consumable receipt with attribution — installed marketplace workflows land in Workflows tab with a purple 'marketplace' badge + 'by ' credit- Studio Workflows → new
Publishbutton per user-built row (server-side signs with your publisher keypair + POSTs to your marketplace seller account) - Publish preflight — checklist card if any prereq missing (keypair · session · seller profile · key registered), each with the exact CLI/URL fix
- Compose engine credits
command_id-bound steps as audited (bridge-composed workflows now PASS instead of UNAUDITED)
Airlock hardening
- Per-command daily rate limits (defaults: stripe 5/day, twilio 25/day, github 50/day, discord 200/day, local 500/day). Config: WS/rate_limits.json. Rate-limited attempts return blocked_by_policy WITHOUT consuming the approval.
- Recipient allowlists per command per field, enforced at Preview time (before approval is offered). Config: WS/allowlists.json. Empty list = deny-all; ['*'] = explicit allow-any.
- Approval-consume moved to before handler invocation — a failing handler still consumes the approval (single-attempt matches the airlock's one-decision contract)
MCP surface
- New tool:
railcall_workflow_compose— external AI turns English into a typed workflow spec, does NOT persist (returns spec for AI to inspect + human to review)
Studio UI
- Sidebar 'Sends' tab shows a live badge with the pending airlock queue count
- First-run wizard on Sends (empty vault + empty queue) — dismissible, links to /docs#airlock
- Programs tab renamed → Workflows (route unchanged for URL back-compat)
- Programs Run button (data-file dropdown → POST /api/flow/run → deep-link to Sends if bridged)
- Prompt secret redaction — 10 formats regex'd out before compose call
- Versioned workflow filenames (no silent overwrites on rebuild)
- h`` template escape-bug fix on Sends (wizard/bridged/lone sections)
Sha-256 (verify before pinning):
3af81ebdfdc7b6540634500ad660a5ff094bbf0370a63d0523b8020afb5762ed railcall_station.tar.gz
engine=6a0fb402 core=73887f55 built=2026-07-23
station-v0.19 · airlock bridge + MCP workflow tools + 10 wired handlers
Studio's Builder now composes bridge-ready specs → each row of a run materializes an airlock preview → human approves → real send + signed receipt.
Ships:
- Builder → airlock bridge: LLM emits
command_id+inputs_templateper step, flow_engine substitutes per row, /api/flow/run creates pending airlock previews grouped by workflow_run - Approval-replay fix: single-use approvals (attempted once, then invalidated regardless of outcome)
- MCP workflow tools:
railcall_workflow_list,railcall_workflow_run,railcall_pending_list— external AI can drive a workflow; human still gates every write in Studio's Sends UI - Vault-backed credential storage with in-Studio Configure UI (no more env-var exports)
- Programs Run button + inline data-file dropdown
- Prompt secret redaction (10 formats: stripe/openai/anthropic/github/slack/aws/google/discord webhook/bearer/pk_*)
- Versioned workflow filenames (no more silent overwrites)
- Client-side elapsed timer during compose
Wired airlock command handlers (10):
local.csv_append,local.file_write— governed local disk writes, atomicdiscord.post_message,slack.post_webhook— webhook messagingwebhook.post_generic— POST to any URL (Zapier catch-hooks, custom services)github.create_issue— Bearer + owner/repo in vaultnotion.add_page— Bearer + Notion-Version header, database_id in vaultlinear.create_issue— GraphQL, raw-key auth (Linear quirk)airtable.create_record— Bearer + base/table in vaultpagerduty.trigger_incident— routing-key auth, Events API v2stripe.create_refund— Bearer + form-encoded + Idempotency-Key from airlock payload_hash (no double-refund on wire retry)twilio.send_sms— HTTP Basic Auth + form bodysendgrid.send_email— Bearer + JSON, X-Message-Id captured for receipt
Auth patterns proven: Bearer, Bearer+version-header, raw-key, HTTP Basic, routing-key-in-body, JSON body, form-encoded body, GraphQL. Any new provider = ~30 line handler in this pattern.
Sha-256 (verify before pinning):
3e19dedb54682b394e32681b5d97957063bb89ac49368fbdc80524a502baed52 railcall_station.tar.gz
engine=f4a0534f3 built=2026-07-23
station-v0.18
Marketplace CLI closes the seller loop.
Ships:
railcall market login/logout/whoami— proper credential flow instead of the env-var placeholderrailcall market publisher init/register+railcall market publishuse the local session with auto-refresh on stale JWT- Points at the live marketplace backend at railcall-marketplace-lggm.onrender.com
Sha-256 (verify before pinning):
642005a21a1978eb4e17ece5a2eeab73837af4046eeea9cfdd44ae842198d15f railcall_station.tar.gz
Companion:
- Marketplace backend deployed; storefront at railcall.ai/marketplace; admin dashboard at /marketplace/admin
engine=9521956fd core=e3bc577
station-v0.17
Marketplace Shape 1 — browse + install for the RailCall workflow marketplace.
Ships:
railcall market list/get/installCLI — browse 7,240 governed workflow templates, install any of the 50 curated exemplars with one command- Live at railcall.ai/marketplace + railcall-core.onrender.com/v1/market/*
- P0-1 CLI seed_store integration (was landed to
mainafter v0.16 but never cut in a release — this closes that gap) - Post-checkout success page now seat-aware for per-seat subscribers
Sha-256 (verify before pinning):
bc80a82ce0fe1c60c40a53cb5bf9be0f903f09bd03ae9ac88b9e0e449938d2df railcall_station.tar.gz
engine=9521956fd core=680e007