Commit graph

808 commits

Author SHA1 Message Date
github-actions[bot]
5a921acca1 chore: Release railwayapp version 4.40.2
Some checks are pending
Cargo audit / security_audit (push) Waiting to run
CI / Check (push) Waiting to run
CI / Lints (push) Waiting to run
CI / Tests (push) Waiting to run
2026-04-21 13:58:34 +00:00
Mahmoud Abdelwahab
8b13b75256
fix: dedupe workspaces in list output (#852)
Workspaces returned by `me.workspaces` and `externalWorkspaces` can
overlap when a user has both workspace membership and project-level
access to the same workspace, causing duplicates in `railway list`,
`railway project list`, and `railway whoami --json`. Dedupe by id
while merging, preferring the Member variant (richer data).

Closes #844, #831.
2026-04-21 15:52:26 +02:00
github-actions[bot]
6cd0335391 chore: Release railwayapp version 4.40.1 2026-04-21 11:35:09 +00:00
samaybar
4db4ff39c3
fix: skip 2FA preflight when using OAuth tokens (#846)
The CLI's 2FA preflight (validate_two_factor_if_enabled) was running
for OAuth-authenticated requests even though backboard's
hasTwoFactorVerification authScope exempts OAuth from 2FA enforcement.

For users with 2FA enabled, the twoFactorInfo query would report
isVerified: true, prompting for a 2FA code. The CLI then calls the
twoFactorInfoValidate mutation, whose resolver explicitly requires
ctx.sessionId != null and throws BadAccessError otherwise. OAuth
requests always have sessionId: null, so the validation is unreachable.

The end-user symptom is: `railway delete` (and any other command
going through the preflight) prompts for a 2FA code on fresh OAuth
logins, then fails with `Bad Access` regardless of whether the code
is correct.

Extend the existing short-circuit to also cover OAuth tokens, mirroring
the behavior for token-based auth and matching the backend's 2FA
exemption.
2026-04-21 13:29:35 +02:00
github-actions[bot]
1f60c8dafe chore: Release railwayapp version 4.40.0
Some checks failed
CI / Check (push) Has been cancelled
CI / Lints (push) Has been cancelled
CI / Tests (push) Has been cancelled
Cargo audit / security_audit (push) Has been cancelled
2026-04-17 16:15:48 +00:00
Cody De Arkland
49941e3841
feat(agent): markdown rendering, spacing fixes, and API endpoint update (#849)
* fix: remove extra blank lines in `railway agent` REPL output

The thinking spinner and the Chunk/ToolCallReady handlers were each
unconditionally emitting a `println!()`, stacking multiple blank lines
between user input and the agent's response. Drop the unconditional
newline before the initial spinner, and only emit a leading newline in
the Chunk/ToolCallReady handlers when a spinner wasn't just cleared
(since `finish_and_clear` already leaves the cursor on a blank line).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(agent): markdown rendering, spacing fixes, and API endpoint update

- Stream raw text during agent responses, then re-render each block with
  termimad on ToolCallReady / WorkflowCompleted for GitHub-style markdown
- Track block start position via crossterm cursor::position so the re-render
  only erases the current block, not prior tool output
- Render a "✓ Done" / "✗ Tool failed" badge after each tool call so the
  indicator stays visible once the spinner finishes
- Insert a blank line between consecutive agent responses within a turn;
  keep the tool spinner directly attached to the preceding text
- Update chat endpoint from /api/v1/chat to /api/v1/agent
- Refactor: bundle streaming state into HumanRenderer, extract
  stream_human / stream_json / tool_badge helpers to drop ~60 lines of
  duplication between run_single_shot and run_repl

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* style: wrap tool-status label across lines for rustfmt

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 09:09:41 -07:00
github-actions[bot]
8e42bf3045 chore: Release railwayapp version 4.39.0
Some checks are pending
Cargo audit / security_audit (push) Waiting to run
CI / Lints (push) Waiting to run
CI / Tests (push) Waiting to run
CI / Check (push) Waiting to run
2026-04-17 07:41:01 +00:00
Mahmoud Abdelwahab
4729ef2808
feat: add railway skills command for installing agent skills (#847)
Installs Railway agent skills so AI coding agents (Claude Code,
Cursor, Codex, OpenCode, and any tool that supports .agents/skills)
can work with Railway more effectively.

- `railway skills install` (alias: `update`) installs globally to
  ~/.agents/skills plus any detected tool directories
- `railway skills remove` removes installed skills
- `--agent <slug>` targets specific tools instead of auto-detection
- Root-level `railway --help` surfaces a tip pointing users at the command

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 16:34:55 +09:00
github-actions[bot]
b1028d453c chore: Release railwayapp version 4.38.0
Some checks are pending
Cargo audit / security_audit (push) Waiting to run
CI / Lints (push) Waiting to run
CI / Check (push) Waiting to run
CI / Tests (push) Waiting to run
2026-04-16 18:06:18 +00:00
Cody De Arkland
2ee9277cc9
feat: add railway agent subcommand for natural-language chat (#841) 2026-04-16 10:57:39 -07:00
github-actions[bot]
7ba08c0954 chore: Release railwayapp version 4.37.4 2026-04-16 01:56:16 +00:00
Brandon
991d8f0b15
Fixing issue #830 (#837)
* Fixing issue #830

* fix: fetch template metadata without auth headers

Railway template lookup endpoints reject authenticated requests, so use a public GraphQL client for template detail and template search requests. Add a regression test so add --database keeps working for logged-in users.

* chore: drop unrelated auth changes

Keep the postgres auth fix focused by removing the stray npm lockfile and the unrelated API token handling change from this PR.
2026-04-15 21:40:07 -04:00
github-actions[bot]
8ab2d5a881 chore: Release railwayapp version 4.37.3 2026-04-15 00:20:29 +00:00
Mahmoud Abdelwahab
9088b31d33
fix: make railway docs work in non-interactive mode (#843)
Print doc URLs (homepage, llms.txt, llms-full.txt) when stdout is not a
TTY instead of bailing. Drop the confirmation prompt in interactive mode
— just open the browser directly. Remove the now-unused
NON_INTERACTIVE_FAILURE constant.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 09:14:33 +09:00
Mahmoud Abdelwahab
8da480744a
fix(ci): use npm trusted publishing via OIDC on Node 24 (#842)
The Publish NPM job has been failing since v4.37.0. Root cause: npm 10
(bundled with Node 22) can sign provenance via OIDC but cannot
authenticate the registry upload itself via trusted publishing — that
requires npm >= 11.5.1. With no NODE_AUTH_TOKEN secret configured, the
publish fell through to an anonymous PUT, which the registry rejected
as a 404 (the standard response for scoped packages when auth is
missing).

Bump the runner to Node 24, which bundles npm 11.x. Drop --provenance
since trusted publishing attaches it automatically.

Also bump actions/setup-node to v6 — v4 runs on Node 20 internally,
which GitHub is deprecating.

This supersedes #838, which removed the npm self-upgrade step after
npm@latest started cross-major upgrading and corrupting itself
mid-install. Using Node 24's bundled npm avoids both the corruption
and the self-upgrade entirely.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:33:39 +09:00
github-actions[bot]
f2dd41f055 chore: Release railwayapp version 4.37.2 2026-04-10 17:45:59 +00:00
Mahmoud Abdelwahab
36622629ac
fix: remove npm self-upgrade step that breaks release CI (#838)
`npm install -g npm@latest` now resolves to npm 11.x, which causes a
fatal cross-major self-upgrade on runners bundling npm 10.x (Node 22).
The step was only needed for OIDC/provenance support, which has been
built into npm since 9.5.0 — Node 22's bundled npm 10.9.7 already
has it.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 02:40:36 +09:00
github-actions[bot]
ceda8313d2 chore: Release railwayapp version 4.37.1 2026-04-10 09:49:39 +00:00
Mahmoud Abdelwahab
d3c91eea54
fix: include build.rs in crates.io package to fix publish (#836)
The `include` directive in Cargo.toml excluded `build.rs`, so the
`BUILD_TARGET` env var was never set during `cargo publish` verification,
causing a compile error.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 18:43:04 +09:00
github-actions[bot]
b2b9a05329 chore: Release railwayapp version 4.37.0 2026-04-06 01:28:11 +00:00
Mahmoud Abdelwahab
44e09c26c0
feat: add auto-update mechanism for CLI (#825)
* feat: add auto-update mechanism for CLI

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: prevent auto-update from blocking CLI exit and fix permission/lock issues

- Don't await download_and_stage on exit; spawn it as fire-and-forget so
  commands like --help return instantly even when an update is available
- Add writability check before attempting self-update download, skipping
  the download entirely for root-owned paths like /usr/local/bin
- Replace acquire-then-drop file lock with PID file guard for package
  manager updates to prevent duplicate concurrent spawns

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: cap update task with timeout and add staleness to PID guard

- Move download_and_stage back into the awaited task so the tokio
  runtime doesn't cancel it on exit, but cap handle_update_task with
  a 5-second timeout so short commands like --help aren't blocked
- Write "PID TIMESTAMP" to the lock file and treat entries older than
  10 minutes as stale, fixing the permanent block on Windows where
  is_pid_alive always returned true

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: cache version check timestamp, separate lock files, and clean up control flow

- Write last_update_check even when CLI is up-to-date, preventing a
  GitHub API call on every invocation
- Use separate file paths for self-update flock (update.lock) and
  package-manager PID guard (package-update.pid)
- Return Ok(None) instead of bail! for "already checked today" since
  it's normal control flow, not an error
- Make Preferences::write atomic via temp file + rename

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: improve robustness, Windows support, and rollback UX for auto-update

- Bypass same-day gate for known-pending versions so timed-out downloads
  retry on the next invocation
- Skip spawning update task when running `autoupdate` subcommand to avoid
  racing with preference changes
- Use nix crate for Unix PID liveness check; add Windows implementation
  via winapi instead of conservative fallback
- Detach child update process from console Ctrl+C on Windows
- Add Windows CREATE_NEW_PROCESS_GROUP flag to package manager spawning
- Use scopeguard for write-probe cleanup in install method detection
- Warn when checksums.txt is missing rather than silently skipping
- Improve rollback: back up current binary first, support multi-candidate
  selection via inquire prompt
- Remove freebsd target triple (no release asset published)
- Use nanosecond-precision tmp file names to reduce collision risk

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

* Updates

* fix: prevent autoupdate disable from applying staged update, handle non-writable shell installs, and preserve retry signal

- Skip try_apply_staged() for both `upgrade` and `autoupdate` subcommands
  so `railway autoupdate disable` doesn't swap the binary before disabling
- Add can_write_binary() check in `railway upgrade` for shell installs in
  non-writable locations (e.g. /usr/local/bin with sudo), guiding users to
  use sudo or reinstall instead of failing with a permission error
- Move clear_latest() from eager call in main to post-success in the
  background task, so a timed-out download preserves the retry signal for
  the next invocation instead of losing it behind the same-day gate

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: clear stale version cache for notification-only installs, lock download_and_stage, and add FreeBSD target

- For installs that can't self-update or auto-run a package manager
  (Homebrew, Cargo, Unknown, non-writable Shell), clear latest_version
  after the notification so the next day's check_update() can discover
  newer releases instead of freezing on the first cached version
- Wrap download_and_stage() with an exclusive file lock (update.lock)
  using double-checked locking to prevent concurrent CLI processes from
  racing on the staged-update directory
- Add FreeBSD x86_64 to detect_target_triple() to match install.sh
  support, preventing shell-installed FreeBSD users from hitting an
  "Unsupported platform" error on upgrade

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: only clear version cache when update actually starts, Windows-safe rename, custom bin dir detection, and rollback target tracking

- download_and_stage returns Result<bool> so callers distinguish "lock
  held" (no work done) from a real staging; failed package-manager
  spawns no longer fall through to the notification-only branch that
  clears the cache
- Add rename_replacing() helper that removes the destination on Windows
  before renaming, fixing silent write failures for preferences.json
  and update.json after the first successful write
- Shell install detection now falls back to any parent directory named
  "bin", covering custom --bin-dir installs (~/bin, /opt/bin, etc.)
- Backup filenames include the target triple; rollback filters
  candidates by current architecture to prevent cross-arch restoration

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: Windows version cache write, rollback guards, CI status reporting, preference persistence, and FreeBSD self-update

- Use rename_replacing() in UpdateCheck::write() so Windows cache clears work
- Add interact_or! and can_write_binary() guards to rollback path
- Report CI-disabled state in autoupdate status
- Create ~/.railway dir in Preferences::write() for clean HOME directories
- Remove FreeBSD from self-update targets until release pipeline publishes assets

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: tighten install detection, ensure ~/.railway exists, gate FreeBSD self-update, and preserve notifications when auto-updates disabled

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: preserve update retry signals, propagate preference write errors, and use atomic Windows rename

- Notification-only installs (Homebrew, Cargo, Unknown) now preserve
  latest_version in the cache so the "new version available" notice
  actually shows on the next invocation instead of being cleared before
  the user sees it.
- Detached package-manager updates no longer clear the retry signal on
  spawn — the cached version persists until the user is actually on the
  new version.
- Failed staged-update applies preserve the staged payload for retry
  instead of deleting it; the 7-day staleness TTL handles permanent
  failures.
- `railway upgrade` now clears the version cache after a successful
  update so the next invocation doesn't redundantly re-download.
- Preferences::write() returns Result so `railway autoupdate disable`
  and `railway telemetry disable` report failures instead of silently
  succeeding.
- Standardize timestamp_nanos_opt() on unwrap_or_default() everywhere.
- Windows rename_replacing uses MoveFileExW(MOVEFILE_REPLACE_EXISTING)
  for a single-syscall atomic replace instead of remove+rename.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: deduplicate update-check persistence with persist_latest helper

Consolidate repeated UpdateCheck write blocks in spawn_update_task into
a single persist_latest() method, and skip redundant writes when
check_update() already persisted the timestamp.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: sort backups by version instead of mtime to fix Windows CI

list_backups relied on filesystem modification times, which required
write access to set in tests and is fragile across platforms. Sort by
the semver version embedded in the backup filename instead.

Also removes low-value tests that only assert trivial string formatting
or compiler-guaranteed match exhaustiveness.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: detach shell downloads, expire stale versions, reduce exit delay, and clarify update message

- Spawn background downloads in a detached child process so they survive
  beyond the parent's exit timeout instead of restarting from zero
- Add download_failures counter to version.json; after 3 consecutive
  failures, clear the cached version to force a fresh API re-check
  (fixes infinite retry loop on yanked/stale releases)
- Reduce exit timeout from 5s to 2s since it now only gates the fast
  API version check, not the download
- Append "(active on next run)" to the auto-update message since the
  current process still executes the old binary

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: extract env var constant, name failure threshold, skip redundant spawns

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: stale comment, probe cleanup, non-TTY gating, Windows .old.exe, rollback prune timing, and check_update timeout

- Correct "5 s" → "2 s" in download_and_stage comment to match handle_update_task
- Simplify can_write_binary probe to unconditional create-then-remove
- Skip spawn_update_task in non-TTY when auto-update is disabled
- Clean up leftover .old.exe on Windows at the start of try_apply_staged
- Defer backup pruning until after rollback succeeds so candidates aren't removed before the picker
- Add 30 s timeout to check_update reqwest client to prevent indefinite hangs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: send update notifications to stderr to avoid corrupting command output

println! in the auto-update and version-banner paths wrote to stdout,
which breaks JSON parsing when the CLI is invoked programmatically
(e.g. by Claude Code piping `railway status --json`).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: clear stale update cache when CLI catches up to cached version

Once the installed binary reaches or surpasses the cached latest_version,
clear the cache so spawn_update_task falls through to a fresh check_update().
This prevents repeated package-manager spawns on every invocation after an
update lands and allows discovery of newer releases on manual install paths.

Also null out stdin on the detached package-manager update process to match
spawn_background_download and fully detach from the terminal.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: always attempt fresh API check so cached version does not become permanent

Previously spawn_update_task skipped check_update() entirely when a
cached version existed, and re-persisted the timestamp on every
invocation. This prevented the same-day gate from ever expiring, so the
CLI would advertise a stale cached version forever and never discover
newer releases.

Now we always call check_update() first (gated to once per UTC day),
falling back to the cached version only within the same day for download
retries.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* style: apply cargo fmt and lint-fix

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: keep cached pending version until staged update is actually applied

background_stage_update() was clearing the cached latest_version
immediately after staging succeeded, but the binary isn't replaced until
try_apply_staged() runs on a later invocation. If that apply step fails,
the user loses both the upgrade banner and future download retries until
the 7-day stale TTL expires. The cache is already cleared on successful
apply in try_apply_staged(), so the staging path should leave it alone.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* style: apply cargo fmt

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: hold update lock across apply in interactive upgrade and rollback

self_update_interactive() dropped the update lock after staging but
before applying, letting a concurrent try_apply_staged() race the binary
replacement. Now the lock is held across both staging and apply.

rollback() was mutating the binary and cleaning staged state without
acquiring update.lock at all, which could interleave with a background
auto-updater. Now it acquires the same lock before replacing the binary.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: correct autoupdate status for non-writable shell installs and close stage-apply race

update_strategy() now checks can_self_update() and can_write_binary()
so `autoupdate status` no longer claims "Background download + auto-swap"
when the binary directory is not writable or the platform is unsupported.

self_update_interactive() now holds a single lock across both
download_and_stage_inner() and apply_staged_update(), eliminating the
window where a concurrent try_apply_staged() could consume the staged
binary between staging and applying.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: drop lock file handle instead of deleting to prevent concurrent lock race

Removing the lock file while the handle is still held unlinks the inode
on Unix, allowing a concurrent process to create a new file at the same
path and acquire its own "exclusive" lock. Replacing remove_file with
drop releases the lock via the OS and leaves the file as an inert
sentinel.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: extract spawn_detached helper and simplify update task logic

- Extract shared spawn_detached() into util/mod.rs, deduplicating the
  detached-process setup from spawn_background_download and
  spawn_package_manager_update
- Remove redundant staged-update check in spawn_background_download
  (child process already checks in download_and_stage)
- Consolidate scattered from_cache/persist_latest branches into a
  single needs_persist flag
- Cache is_auto_update_disabled() result in main() instead of calling
  it twice
- Use persist_latest(None) in check_update's up-to-date branch
- Trim narrating comments

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: skip rolled-back version in auto-update instead of pausing entirely

After rollback, record the rolled-back-from version as skipped in
version.json. Auto-update skips only that version and resumes normally
once a newer release is published. This avoids silent staleness from
a full pause while still respecting the user's rollback intent.

- Add skipped_version field to UpdateCheck (serde-default for compat)
- Record skip in rollback(), guard try_apply_staged() against the race
  where a pre-rollback detached download stages the skipped version
- Clear skip on successful update (clear_after_update) and autoupdate enable
- Suppress "new version available" notification for skipped version
- Show skipped version in autoupdate status

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: close concurrency races in rollback, package-manager guard, and failure reset

- Acquire update.lock before backup/picker in rollback() so a concurrent
  try_apply_staged() cannot swap the binary during interactive selection
- Serialize the PID-check-spawn-write sequence in spawn_package_manager_update()
  with a file lock to prevent two CLI invocations from both launching updaters
- Clear last_update_check alongside latest_version after 3 download failures
  so the same-day gate does not suppress the fresh API re-check

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: add checksum generation, respect disable preference, and lock staged cleanup

- Add generate-checksums job to release workflow that produces checksums.txt
  with SHA-256 hashes for all release assets, making checksum verification
  functional instead of always falling back to the skip path
- Gate update polling and "New version available" banner on auto_update_enabled
  so disabled users no longer hit the GitHub API or see nagging banners
- Acquire update.lock in autoupdate disable before cleaning staged updates so
  an in-flight background download cannot re-stage after cleanup returns

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* revert: remove checksums.txt workflow change (pre-existing, out of scope)

The missing checksums.txt in the release pipeline predates this PR.
Reverting the workflow change to keep this PR focused on auto-update fixes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Revert "revert: remove checksums.txt workflow change (pre-existing, out of scope)"

This reverts commit e1e76a1ea2.

* fix: harden auto-update safety for non-TTY, install detection, and permissions

- Gate background updates and staged-apply on is_tty so cron jobs,
  scripts, and piped invocations never silently self-mutate
- Resolve symlinks in InstallMethod::detect() to prevent Intel Homebrew
  misclassification when current_exe() returns the symlink path
- Replace catch-all bin/ Shell detection with explicit allowlist of
  known shell-installer directories to avoid overwriting version-manager
  binaries
- Probe binary directory writability in can_auto_run_package_manager()
  to prevent repeated doomed npm/Bun/Scoop updates on sudo installs
- Acquire package-update lock in autoupdate disable to wait for
  in-flight package manager updates before returning

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: wait for detached updater by PID, restore custom bin-dir support, fix status accuracy

- autoupdate disable now polls the actual child PID instead of
  acquiring a lock the detached process never holds, so it truly
  waits for in-flight npm/Bun/Scoop updates (30s timeout with warning)
- Replace install-method allowlist with version-manager exclusion list
  (asdf, mise, rtx, proto, volta, fnm, nodenv, rbenv, pyenv) and
  restore the */bin/ catch-all so custom --bin-dir installs from
  install.sh are correctly classified as Shell again
- update_strategy() now reflects the writability check for
  npm/Bun/Scoop, showing "Notification only" when the binary
  directory is not writable instead of overpromising

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: preserve rollback guard on enable, recover from missing staged binary, quiesce detached child on disable

- `railway autoupdate enable` no longer clears skipped_version; the
  rollback guard persists until a newer release is applied
- Staged-update fast paths now verify the binary exists before
  short-circuiting; missing binary cleans stale metadata immediately
- Detached background child re-checks auto-update preferences before
  downloading and again after acquiring the update lock

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: exclude check_updates from auto-update side effects

check_updates is a read-only command but was not in the
update-management guard, so it could apply staged binaries and
spawn background downloads.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* style: reformat matches! macro (cargo fmt)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: close pkg-manager race on disable, unblock same-day discovery after rollback, fix Windows upgrade instructions

- spawn_package_manager_update re-checks is_auto_update_disabled()
  after acquiring its lock, preventing a concurrent invocation from
  spawning an updater after the user ran autoupdate disable
- skip_version() now clears last_update_check so the next invocation
  performs a fresh API check and can discover a newer release published
  the same day as the rollback
- Upgrade/rollback non-writable instructions now show Windows-appropriate
  guidance (run as Administrator) instead of sudo/bash commands

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: keep checking for releases while skipped version is latest, acquire package-update lock in disable

- check_update() no longer arms the daily gate when the discovered
  version matches skipped_version, so a fix release published later
  the same day is discovered on the next invocation
- autoupdate disable now acquires package_update_lock (blocking) before
  reading the PID file, closing the race where spawn_package_manager_update
  writes the PID after disable already looked

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: preserve package-update PID file when child outlives the 30s timeout

disable was unconditionally removing the PID file even when the
detached updater was still alive. This left no in-flight marker,
so re-enabling auto-updates could launch a duplicate updater.
Now the PID file is only removed once the child has actually exited.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: ensure ~/.railway directory exists before creating rollback lock file

On a fresh install where ~/.railway has never been written,
rollback would fail with "Failed to create update lock file"
instead of reporting no backups available.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: detect custom CARGO_INSTALL_ROOT installs via .crates.toml marker

The */bin catch-all could misclassify a custom Cargo install root
(e.g. CARGO_INSTALL_ROOT=~/tools) as a shell install, allowing the
auto-updater to self-replace a Cargo-managed binary. Now checks for
Cargo's .crates.toml marker in the parent of bin/ before falling
through to Shell.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: re-read version state after network call to prevent rollback skip loss, check Cargo marker before shell-path heuristic

check_update() loaded version.json, did a network call (up to 30s), then
wrote the stale snapshot back — silently overwriting a skipped_version set
by a concurrent rollback.  Now re-reads from disk after the network call.

InstallMethod::detect() matched /usr/local/bin before the .crates.toml
marker check, so CARGO_INSTALL_ROOT=/usr/local installs were misclassified
as Shell.  Moved the marker check above the shell-path heuristic.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: don't stamp daily gate when clearing stale version, exclude pnpm from npm detection

clear_latest() called persist_latest(None) which set last_update_check=now,
preventing the background task from discovering a hotfix published later the
same day after a manual upgrade.  Now clears the cached version without
stamping the gate.

pnpm global paths contain "npm" as a substring, causing misclassification
as InstallMethod::Npm and driving updates through the wrong package manager.
Added an early pnpm check that falls back to Unknown.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: serialize interactive upgrade with background package-manager updates

run_upgrade_command() now acquires the same file lock and checks the PID
file used by spawn_package_manager_update(), preventing concurrent
package-manager processes against the same global install when a user
runs `railway upgrade` while a background auto-update is in flight.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: prevent update side effects on help/error paths and retain cached version on API failure

Use raw args to detect update-management subcommands so that clap
DisplayHelp/DisplayVersion error paths (e.g. `railway upgrade --help`)
no longer trigger try_apply_staged() as a side effect.

Match on the Result from check_update() instead of using `?` so that
API errors fall back to the cached known_version for retry rather than
short-circuiting the entire update task.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: skip auto-update pipeline entirely on help/version/parse-error paths

--help, --version, and invalid-input paths now bypass both
try_apply_staged() and the background update spawn, ensuring they are
truly read-only with zero added latency.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: extract parse_pid_file helper and remove dead .crates.toml check

- Extract `parse_pid_file()` in check_update.rs to deduplicate PID file
  parsing across autoupdate, upgrade, and check_update modules
- Remove unreachable `.crates.toml` guard in install_method.rs catch-all
  (the same check already fires earlier in detect())

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: don't stamp daily gate for skipped version, make checksum upload idempotent

- After rollback, an API failure would fall back to the cached (skipped)
  version and call persist_latest(), arming the daily gate. This prevented
  re-checking for a newer release until the next day. Now skipped versions
  skip persist_latest so the API is re-checked on every invocation.
- Add --clobber to gh release upload so re-running the checksums job
  doesn't fail on an existing asset.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: skip update side effects on bare `railway`, use compile-time target triple

- Bare `railway` (no subcommand) now skips try_apply_staged() and the
  background updater, matching the read-only behavior of --help/--version.
  Previously a first-time user typing `railway` to explore would trigger
  update side effects before seeing help.

- detect_target_triple() now uses the compile-time BUILD_TARGET from
  build.rs instead of runtime OS/arch guessing. This ensures the
  self-updater fetches the correct ABI variant (e.g. a binary built for
  x86_64-unknown-linux-gnu will not be replaced with a musl build).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: treat `railway help` as read-only, fix i686-pc-windows-gnu asset format

- Add "help" to the read-only invocation guard so `railway help` skips
  try_apply_staged() and the background updater, matching the behavior
  of --help and bare `railway`.

- i686-pc-windows-gnu is cross-compiled on Linux and only ships as
  .tar.gz (no .zip). The asset name logic now accounts for this, and
  extraction derives the format from the asset name rather than
  re-checking the target.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: clear daily gate on version catch-up, restore shell upgrade path, eager download spawn

- clear_latest() now resets last_update_check so that same-day hotfixes
  are discovered after the user catches up to a cached version.

- Shell installs on unsupported self-update platforms (e.g. FreeBSD) now
  get a dedicated match arm showing the reinstall command instead of
  falling through to the vague catch-all message.

- spawn_update_task now starts the background download from the cached
  version before the API call, so the 2s exit timeout cannot strand the
  spawn on slow networks. The API check still runs to refresh the cache.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: only eagerly spawn download when same-day gate is armed

The eager download spawn could race with a newer version discovered by
the API: the cached-version child holds the update lock, so the
newer-version child exits immediately, and try_apply_staged would apply
the stale cached release on the next run.

Fix: only eagerly spawn when the same-day gate is armed (last_update_check
is today). In that case check_update(false) returns Ok(None) instantly
without a network request, so the API cannot discover a newer version that
would conflict. When the gate is NOT armed, the API call goes over the
network and may return a newer release — defer the spawn to after the
check completes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: ensure parent directory exists before creating detached-process log file

spawn_detached() creates the log file without ensuring the parent
directory exists. On a fresh install where ~/.railway doesn't exist yet,
this fails silently (the spawn result is ignored), preventing the
background download from starting.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: show update banner when auto-update disabled, apply staged offline, add PID TTL to disable

- Version discovery and the "New version available" banner now run
  regardless of auto-update preference. Disabling auto-update stops
  automatic installation, not release awareness.

- `railway upgrade` now falls back to an already-staged update when the
  network check fails, so offline users can apply a previously
  downloaded binary.

- `autoupdate disable` now applies the 10-minute PID file TTL before
  trusting the stored PID, consistent with upgrade and
  spawn_package_manager_update. Prevents blocking 30s on a recycled PID.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: validate staged update before offline apply in interactive upgrade

The fallback path in self_update_interactive now applies the same guards
as try_apply_staged: rejects stale entries, wrong-platform binaries,
versions <= current, and versions the user rolled back from. Prevents
offline `railway upgrade` from downgrading or re-applying a skipped
version.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: simplify auto-update design after review

- Switch version check gate from calendar-day UTC to fixed 12h window
- Remove checksums.txt machinery (code, tests, workflow job) — co-located
  checksums don't add security; TLS handles integrity in transit
- Reduce exit-delay timeout from 2s to 1s for version check
- Simplify `autoupdate disable` — set flag and clean staged, no PID polling
- Run version check in non-TTY to keep cache fresh for script-heavy users
- Suppress update banner when disabled via env var or CI, keep for preference
- Extract `validate_staged()` to deduplicate safety checks
- Enrich `autoupdate status` with pipeline state (latest version, staged,
  last check time, in-flight PID)
- Add `autoupdate skip` subcommand for package-manager installs
- Split download timeout: 30s background, 120s interactive
- Replace count-based package-manager retry (5 attempts) with time gate (1/hr)
- Add telemetry event for silent auto-update apply
- Clean up orphaned generate-checksums workflow job

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: simplify PID liveness check, remove unnecessary duplication

- Extract `is_package_update_running()` helper to replace 3 identical
  PID-file-parse + age-check + liveness-check blocks
- Extract `PID_STALENESS_TTL_SECS` constant (was magic number 600)
- Remove unnecessary clone in `autoupdate skip`
- Remove TOCTOU `.exists()` check before `hard_link` in backup
- Remove unnecessary comment

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address review findings — TOCTOU, handle leak, error visibility

- Move validate_staged() inside the exclusive lock in try_apply_staged()
  to close TOCTOU window where another process could delete the staged
  binary between validation and apply
- Use std::mem::forget on detached Child handles to avoid leaking OS
  resources on Windows (both package-manager and background-download paths)
- Use rename_replacing() in replace_binary() on Unix for consistency
- Replace silent `let _ = write()` in cache mutation methods with
  try_write() that logs warnings on failure

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: collapse clippy warning, document intentional handle leaks, defer Windows cleanup

- Collapse nested `if` in spawn_update_task to fix clippy::collapsible_if
- Add comment in download_and_stage_inner explaining the duplicate
  staged-version check is the authoritative post-lock re-check
- Document std::mem::forget(child) on both detached-process call sites
- Move Windows clean_old_binary() to only run after successful apply
  instead of on every try_apply_staged entry

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: skip API check in disabled non-TTY sessions, validate staged binary, add --locked to cargo install

- Non-TTY sessions with auto-update disabled no longer make a GitHub API
  call or pay the exit-time budget (CI/scripts).
- validate_staged() now checks the staged binary exists on disk before
  reporting the update as ready to apply.
- Cargo upgrade command includes --locked to match documented install path
  and prevent resolver drift.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: simplify auto-update code — deduplicate dispatch, extract helpers, reduce boilerplate

- Extract `write_atomic()` in util/mod.rs, replacing 3 duplicated
  temp-file + rename write implementations
- Extract `try_dispatch_update()` helper and `UpdateContext` struct,
  eliminating duplicated dispatch logic and double InstallMethod::detect()
- Add `UpdateCheck::mutate()` to absorb read-modify-write boilerplate
  from 5 static methods
- Collapse 3 near-identical disabled-state branches in autoupdate status
  into one computed `disabled_reason`
- Trim WHAT-narration comments to concise WHY reasoning

No behavior changes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 10:21:50 +09:00
github-actions[bot]
56bbd07c74 chore: Release railwayapp version 4.36.1 2026-04-03 09:51:06 +00:00
Jake Cooper
15e738f2fa
Show telemetry notice on install instead of first run (#832)
Move the telemetry opt-out notice from a runtime banner (shown on first
CLI command) to the install script so users see it exactly once at
install time. Remove the Notices JSON persistence that tracked whether
the banner had been shown.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 18:45:07 +09:00
github-actions[bot]
b5130a3c4e chore: Release railwayapp version 4.36.0 2026-03-31 21:24:10 +00:00
Mahmoud Abdelwahab
974aa2cd26
feat: add environment list subcommand (#828)
* feat: add `environment list` subcommand

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* style: improve environment list output spacing and separators

Add blank lines and a horizontal rule separator between environment
sections for better visual readability.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 23:18:08 +02:00
github-actions[bot]
ec1ea2e371 chore: Release railwayapp version 4.35.2 2026-03-31 03:59:03 +00:00
Brody Over
898a59d63a
feat(delete): use projectScheduleDelete mutation for soft deletion (#827)
Switch from immediate `projectDelete` to `projectScheduleDelete` which
provides a 48-hour grace period before the project is permanently removed.

Made-with: Cursor
2026-03-30 23:53:31 -04:00
github-actions[bot]
5b75f1884c chore: Release railwayapp version 4.35.1 2026-03-30 09:15:48 +00:00
Mahmoud Abdelwahab
3ca7e63637
fix(config): make RAILWAY_ENVIRONMENT_ID optional for project-level commands (#826)
* fix(config): make RAILWAY_ENVIRONMENT_ID optional for project-level commands

Commands like `env new`, `env delete`, and `env link` only need a
project ID, not an environment. The previous validation required both
RAILWAY_PROJECT_ID and RAILWAY_ENVIRONMENT_ID to be set together,
which made these commands unusable with just RAILWAY_PROJECT_ID.

Make `LinkedProject.environment` an `Option<String>` and remove the
blanket XOR validation. Commands that need an environment now error
at the point of use with a clear message, while project-level
commands work with just RAILWAY_PROJECT_ID set.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: context-aware error messages for missing environment

When RAILWAY_PROJECT_ID is set, tell the user to set
RAILWAY_ENVIRONMENT_ID (since `railway environment` writes to local
config which is ignored when env vars take priority). When using local
config, suggest `railway environment` as before.

Also consolidates duplicated error messages across callers into the
central `environment_id()` helper.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: simplify error message for missing environment

The environment is only None when using env-var targeting (local config
always has an environment set via link_project), so drop the
unreachable branch.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: reject env-only override and defer environment preflight

P1: Reject RAILWAY_ENVIRONMENT_ID set without RAILWAY_PROJECT_ID
instead of silently falling back to local config.

P2: Make ensure_project_and_environment_exist() skip the environment
check when no environment is linked. Callers that accept --environment
resolve and validate it themselves, so the preflight no longer blocks
them.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: add tests for env var project targeting validation

Extract resolve_env_var_project() from get_linked_project() so the
env var validation logic can be tested without Configs/auth/API deps.

Two tests:
- PROJECT_ID alone → succeeds with environment None
- ENVIRONMENT_ID alone → rejected

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: improve error messages and graceful status for missing environment

Provide actionable guidance (set env var, use --environment, or run
railway environment) and show "None" in status instead of crashing
when no environment is linked.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* style: fix rustfmt formatting in status.rs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: merge locally linked environment in project-only env-var mode

When RAILWAY_PROJECT_ID is set without RAILWAY_ENVIRONMENT_ID, fall
back to the environment from the local config so that `railway
environment` remains a valid remediation path.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: only merge local environment when project IDs match

Prevents silently using project A's environment with project B when
RAILWAY_PROJECT_ID overrides the locally linked project.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: resolve stale env name, nested dir lookup, and down panic in env-var mode

- Don't carry local environment_name when RAILWAY_ENVIRONMENT_ID override
  is set, preventing preflight from validating against the wrong environment
- Use ancestor-walking lookup for local project so nested directories still
  find the linked config in env-var mode
- Replace expect() with fallback in `down` command to avoid panic when
  project/environment names are absent

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 11:05:18 +02:00
github-actions[bot]
cc8cc6ec0d chore: Release railwayapp version 4.35.0 2026-03-25 20:06:36 +00:00
Mahmoud Abdelwahab
8ab159ce6b
feat(config): allow project targeting via env vars (#823)
* feat(config): allow project targeting via env vars without railway link

Add support for RAILWAY_PROJECT_ID, RAILWAY_ENVIRONMENT_ID, and
RAILWAY_SERVICE_ID env vars to configure the target project/environment
without requiring `railway link`. Useful for CI/CD and scripting.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* run cargo fmt

* fix(config): update environment variable checks for project targeting

Modify the `has_env_var_project_config` function to return true if either `RAILWAY_PROJECT_ID` or `RAILWAY_ENVIRONMENT_ID` is set, allowing for more flexible project targeting. Additionally, enforce that both environment variables must be set together in the relevant section of the code, providing clearer error messaging when one is missing.

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 22:00:47 +02:00
github-actions[bot]
34a635ab34 chore: Release railwayapp version 4.34.0 2026-03-25 18:44:46 +00:00
Mahmoud Abdelwahab
c276e4ec2b
feat(auth): replace custom login flow with OAuth 2.0 (PKCE + device flow) (#822)
Replace the GraphQL-based LoginSession flow and hyper HTTP server with
standard OAuth 2.0: Authorization Code + PKCE for browser login, and
Device Authorization Grant (RFC 8628) for browserless. Adds automatic
token refresh before commands, CSRF state verification, and proper
error variants. Removes hyper, hyper-util, http-body-util, and hostname
dependencies in favor of raw TCP + sha2 for PKCE.

Browser login hardened with OS-assigned port, looped callback handler
for preconnect resilience, 5-minute timeout, and prompt=consent to
ensure refresh tokens are always issued.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 20:38:18 +02:00
github-actions[bot]
d858c26652 chore: Release railwayapp version 4.33.0 2026-03-18 17:03:05 +00:00
Paulo Cabral Sanz
81cd7d0ada
feat(ssh): native SSH and key management commands (#809)
* feat(ssh): add native SSH using serviceInstanceId

Adds support for native SSH connections using the new flow:
`ssh <serviceInstanceId>@ssh.railway.com`

Changes:
- Add native SSH module that uses serviceInstanceId for routing
- Add SSH key management (auto-detect local keys, register with Railway)
- Add GraphQL queries for SSH keys and service instances
- Native SSH is used by default when local SSH keys exist
- Add --relay flag to force WebSocket fallback mode

The native SSH flow:
1. Checks for local SSH keys (~/.ssh/id_*.pub)
2. Ensures key is registered with Railway (prompts or auto-registers)
3. Gets serviceInstanceId via GraphQL
4. Runs ssh <serviceInstanceId>@ssh.railway.com

* fix(ssh): use relay mode for command execution

Railway's SSH proxy doesn't forward exec commands through the QUIC
tunnel, so command execution requires relay mode. Native SSH is now
only used for interactive shells where it works correctly.

- Commands use relay mode (railway ssh <command>)
- Interactive shells use native SSH (railway ssh)
- Tmux sessions continue using relay mode

* feat(ssh): show which SSH key is being used

Display the SSH key path when connecting to help users understand
which key is being used for authentication.

* refactor(ssh): clean up unused code and fix clippy warnings

- Remove unused run_native_ssh_with_tmux (exec commands not supported)
- Remove unused find_registered_local_key and ensure_ssh_key_registered
- Fix &PathBuf -> &Path clippy warnings
- Keep tmux sessions using WebSocket relay since SSH exec isn't supported

* refactor(ssh): scan ~/.ssh/ for all .pub files instead of hardcoding

- Dynamically scans ~/.ssh/ directory for all .pub files
- Filters to supported key types (ed25519, ecdsa, rsa, dss)
- Sorts by key type preference (ed25519 first)

* feat(ssh): add key management commands

- Add `railway ssh keys` to list registered SSH keys
- Add `railway ssh keys add` to register a local key
- Add `railway ssh keys remove` to delete a registered key
- Shows which local keys match registered keys
- Supports 2FA for key deletion

* feat(ssh): add SSH key management commands

Add `railway ssh keys` command with subcommands:
- `list` (default): Show registered, GitHub, and local SSH keys
- `add`: Register a local SSH key with Railway
- `remove`: Delete a registered SSH key
- `github`: Import SSH keys from GitHub account

Also removes unused LogFormat::Simple variant.

* fix(ssh): format command hints on separate lines

* refactor(ssh): use direct serviceInstance query instead of listing all

* refactor(ssh): make native SSH opt-in via --native flag

WebSocket relay is now the default SSH method. Users can opt into
native SSH with --native flag when they want direct SSH connections.

* feat(ssh): add command execution support to native SSH mode

Pass command arguments to the ssh binary when using --native flag,
enabling commands like `railway ssh --native echo hello`.

* fix(ssh): improve 2FA handling and key registration UX

- Use validateTwoFactor mutation before delete instead of passing code to sshPublicKeyDelete
- Update GraphQL mutation to remove unused code parameter
- Fix 2FA error detection to be case-insensitive and match "two factor" string
- Replace silent non-TTY key registration with explicit error directing user to register manually
- Simplify run_native_ssh to always inherit stdio regardless of command mode

* fix(ssh): require user to select which SSH key to register

* style: run cargo fmt
2026-03-18 13:57:37 -03:00
github-actions[bot]
b24f48db41 chore: Release railwayapp version 4.32.0 2026-03-18 06:17:16 +00:00
Mahmoud Abdelwahab
a7aeeccfb3
Adds an experimental MCP server (#813)
* Add GraphQL queries and mutations for MCP server

Add Metrics and Templates queries for service observability and template
search. Add ServiceInstanceUpdate mutation for updating service settings
like build command, replicas, and health check path.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Extract upload logic from up command into controller

Move tarball creation and upload logic into src/controllers/upload.rs so
it can be reused by both the up CLI command and the MCP deploy tool.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Change log callbacks from Fn to FnMut

The MCP log handler collects logs into a Vec, which requires mutating
captured state. Fn closures don't allow this — FnMut does. Existing
callers are unaffected since every Fn is a valid FnMut.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Add MCP server for AI-agent access to Railway

Expose Railway operations (deploy, logs, variables, domains, templates,
metrics, etc.) as MCP tools so AI agents can manage infrastructure
programmatically. Uses the rmcp crate for the MCP protocol.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Add more fields to update_service MCP tool

Expose cron_schedule, dockerfile_path, healthcheck_timeout,
restart_policy_type, restart_policy_max_retries, pre_deploy_command,
region, railway_config_file, and watch_patterns.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Fix MCP server: deploy resolution, linked-context precedence, and workspace auto-detect

- Return clear error with available services when deploy gets an unknown service name
- Prefer locally-linked project over token-derived project in MCP context resolution
- Auto-detect team workspace when create_project is called without workspace_id

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Fix docs_search and split into search + fetch tools

- Replace GitHub API (401 auth failures) with public sitemap for page listing
- Fix content path from src/docs/ to content/ to match actual repo structure
- Use plain HTTP client for external fetches (Railway auth headers caused GitHub 401s)
- Split into docs_search (returns top 5 URLs) and docs_fetch (reads full page)
- Improve scoring: exact path segment matches rank higher than substrings

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Hide confirm field from MCP schema to prevent AI agents from auto-setting it

AI agents were reading "Requires confirm: true" in tool descriptions and
auto-populating confirm on the first call, bypassing the destructive action
safety guard. Fix by adding #[schemars(skip)] to the confirm field on
RemoveServiceParams, RemoveBucketParams, and RemoveVolumeParams so it's
hidden from the JSON schema (serde still deserializes it, defaulting to
false). Updated descriptions to say "Returns a preview first" instead.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Reject create_service when both source_repo and source_image are provided

The backend silently picks repo over image when both are set (via an
if/else chain in the serviceCreate resolver), dropping the image with
no error or warning. This is confusing for MCP agents that may not
realize one source was ignored. Validate early in the MCP layer to
give a clear, actionable error instead of silent precedence.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Add help text for mcp command

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Drop vulnerable time crate by disabling unused serde_with features

serde_with was pulling in the time crate (RUSTSEC-2026-0009, DoS via
stack exhaustion in RFC 2822 parsing) through default features. We only
use skip_serializing_none from the macros feature, so disable defaults
and enable only macros. This removes time and several other unused
transitive deps (hex, indexmap v1, deranged, powerfmt, num-conv).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 08:10:03 +02:00
Denis Sermukhamedov
33b1e91cc5
Add http logs (#810)
* Add HTTP log mode to logs command

* Add coverage for HTTP log examples

* Fix HTTP log streaming subscription

* Add full HTTP log field parity and discoverable filter flags

- Fix HTTP log fetch to use anchor-based pagination (beforeLimit/anchorDate)
  instead of limit/startDate/endDate which the backend ignores
- Add all 20 HttpLog fields to GraphQL fragments for full JSON output
  parity with the dashboard
- Add typed filter flags: --method (enum-validated), --status (supports
  200, >=400, 500..599), --path, --request-id (all require --http)
- Expand --filter help to list all filterable fields and operators

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Detect deployment removal during HTTP log streaming

When a deployment is removed while streaming HTTP logs, the CLI now
exits cleanly with a message instead of silently retrying forever.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Mahmoud Abdelwahab <m@mahmoudw.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 21:57:45 +02:00
M. Casqueira
87605d5caf
fix: update nix shell pins to provide Rust 1.94 for edition2024 support (#812) 2026-03-13 15:07:44 -04:00
Zicklag
d41b2e4dfb
fix: fix connection errors when using custom system certificates and an http proxy. (#807)
When trying to use the Railway CLI behind an HTTP proxy that requires
a custom certificate ( such as some VPNs ), the CLI would previously
fail with a certificate error. This adds the `rustls-tls-native-roots`
feature reqwest to trust the native platform's system certificate store to
fix that.

Railway commands that require a websocket connection were also
failing to go through the proxy, because while `reqwest` automatically
sends requests through the proxy `async-tungstenite` would not. This
removes `async-tungstenite` in favor of `reqwest-websocket` which is
simpler and uses `reqwest` to properly send the initial HTTP request
through the proxy before the websocket upgrade.
2026-03-12 12:01:23 -04:00
github-actions[bot]
6b9b52eb17 chore: Release railwayapp version 4.31.0 2026-03-06 19:41:42 +00:00
Mahmoud Abdelwahab
a2f8a69cb3
feat: add railway bucket command for managing buckets (#806)
* feat: add `railway bucket` command for managing Railway buckets

Supports create, delete, list, info, credentials, and rename subcommands with --json output, interactive/non-interactive modes, and automatic commit vs stage detection based on pending environment changes.
2026-03-06 21:34:03 +02:00
Ryan McHenry
72c52012d7
Adding logic to handle selection events past end of log line length. Build passes, tests passes. (#805) 2026-02-27 14:14:25 -05:00
github-actions[bot]
427c325f22 chore: Release railwayapp version 4.30.5 2026-02-24 19:27:36 +00:00
Jake Runzer
5df54c18e9
fix: set correct Content-Type for upload in railway up (#804)
The upload body is gzipped tarball, not multipart form data.
2026-02-24 14:22:10 -05:00
github-actions[bot]
49f73e0178 chore: Release railwayapp version 4.30.4 2026-02-23 17:52:21 +00:00
Faraz Patankar
dc16439423
feat: add railway telemetry command (enable/disable/status) (#802)
Adds a user-friendly CLI command to manage telemetry preferences,
stored persistently in ~/.railway/preferences.json. Previously the
only way to opt out was via environment variables.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 12:46:22 -05:00
github-actions[bot]
a8a5afe145 chore: Release railwayapp version 4.30.3 2026-02-17 01:15:39 +00:00
Brody Over
a69484e4b7
print txt record for custom domain (#797) 2026-02-16 20:10:12 -05:00
github-actions[bot]
7650d29f22 chore: Release railwayapp version 4.30.2 2026-02-14 03:18:16 +00:00