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>
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>
* 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>
* 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>
* 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.
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>
* Add per-command telemetry via cliEventTrack mutation
Sends command name, subcommand, duration, success/error, OS, arch,
CLI version, and CI flag to the Railway API after each invocation.
Silently skipped when unauthenticated, on network failure, or when
the user opts out via DO_NOT_TRACK=1.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Migrate telemetry to typed graphql_client mutation
Pull updated schema with cliEventTrack mutation and replace the raw
JSON POST with typed codegen via graphql_client, matching the rest
of the codebase. Also adds DNS_RECORD_TYPE_TXT from the schema update.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Add one-time telemetry notice on first run
Shows a notice to stderr informing users about CLI usage data
collection, with DO_NOT_TRACK=1 opt-out. Persisted to
~/.railway/notices.json so it only displays once. Suppressed
in CI/non-TTY and when telemetry is already disabled.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Support RAILWAY_NO_TELEMETRY env var for opting out of telemetry
Adds RAILWAY_NO_TELEMETRY=1 as a Railway-specific alternative to
DO_NOT_TRACK=1 for disabling CLI telemetry. Updates the notice
text to mention both variables.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Fix telemetry docs link to /cli/telemetry
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Fix cargo fmt --all formatting in telemetry notice
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Extract the restart functionality from `railway redeploy --restart` into
a dedicated `railway restart` command for better discoverability and
simpler usage.
Usage:
railway restart # Restart linked service
railway restart -s backend # Restart specific service
railway restart -y # Skip confirmation
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Adds `railway local` as an alias for `railway run` to make it clearer
that the command runs locally with Railway environment variables
injected, rather than running on Railway's servers (like Heroku's run).
Users can now use either:
railway run npm start
railway local npm start
Closes#715
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Adds a new `delete` command that allows users to delete projects from the CLI.
Features:
- Interactive project selection when no project specified
- Confirmation prompt before deletion (can be skipped with -y/--yes)
- 2FA support for users with two-factor authentication enabled
- Works in CI with --project and --yes flags
Closes#695
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* init develop command
* develop: use service names, random ports, project-specific dir
- extract environment config types to controllers/environment_config.rs
- use slugified service names instead of IDs in compose
- generate deterministic external ports (10000-60000) to avoid conflicts
- save compose to ~/.railway/develop/<project_id>/
- always run detached
* develop: show service names and URLs in output
* develop: resolve variables and override Railway networking vars
- Fetch resolved variables via VariablesForServiceDeployment API
- Override RAILWAY_PRIVATE_DOMAIN with docker-compose service slug
- Override RAILWAY_PUBLIC_DOMAIN and RAILWAY_TCP_PROXY_DOMAIN with localhost
- Override RAILWAY_TCP_PROXY_PORT with mapped local port
- Replace *.railway.internal refs in values with service slugs
- Filter deprecated vars (RAILWAY_STATIC_URL, RAILWAY_SERVICE_*_URL)
- Add docker-compose networking for inter-container communication
- Escape $ as $$ in commands for docker-compose interpolation
* develop: add volume support and down subcommand
- Add volumes field to docker-compose output (persist through up/down)
- Add `develop down` subcommand to stop containers
- Use environment ID for compose dir and volume names
- Show volume mount paths in service summary
* run: use local services when develop is active
Add --no-local flag to skip. Extracts shared variable override logic
into controllers/local_override.rs for reuse between run and develop.
* develop down: add --clean flag to remove volumes and files
* restart services on failure
* develop: add local HTTPS with mkcert and pretty URLs
- auto-generate TLS certs via mkcert for {project}.railway.dev
- add Caddy reverse proxy for HTTPS termination
- set RAILWAY_PUBLIC_DOMAIN to https://{domain}:{port}
- auto-add /etc/hosts entry on develop up
- remove hosts entry on develop down --clean
- skip cert generation if already exists
* develop: use .localhost TLD instead of .railway.dev
.localhost resolves to 127.0.0.1 per RFC 6761, eliminating need for
/etc/hosts manipulation and sudo prompts
* process manager
* develop: support port 443 for prettier public URLs
- Try binding to port 443 at startup for cleaner URLs
- Port 443 mode: https://{service}.{project}.railway.localhost
- Fallback mode: https://{project}.railway.localhost:{port}
- Generate wildcard certs (*.project.railway.localhost) for port 443
- SNI-based routing in Caddy when using port 443
- Add http:// prefix to private domain display
* develop: add session lock to prevent concurrent code service runs
- Add DevelopSessionLock using fs2 file locking to prevent multiple
develop sessions running code services for the same environment
- Check existing https_mode before falling back from port 443
- Lock auto-releases on drop or process crash
* develop: refactor into testable module structure
- Extract shared types to controllers/config/ (EnvironmentConfig)
- Create controllers/develop/ with submodules:
- ports.rs: slugify, generate_port + tests
- variables.rs: override_railway_vars + tests
- session.rs: DevelopSessionLock + tests
- traits.rs: EnvironmentDataProvider, CommandRunner + mocks
- compose.rs: DockerCompose types, build_port_infos
- https_proxy.rs: HttpsConfig, certs, Caddyfile gen
- code_runner.rs: ProcessManager
- local_config.rs: LocalDevConfig
- Add async-trait dependency for trait abstractions
- Old controller files re-export for backward compat
* develop: remove unused traits and cleanup backward compat stubs
- Delete unused EnvironmentDataProvider and CommandRunner traits (371 lines)
- Add fetch_environment_config() function to controllers/config
- Update develop.rs to use centralized fetch function
- Delete empty stub files: process_manager.rs, develop_lock.rs,
local_https.rs, local_dev_config.rs
- Update local_override.rs to import directly from develop module
* develop: remove unused get_env_vars, cleanup dead_code attributes
- Remove unused get_env_vars method from ServiceInstance
- Remove unnecessary #![allow(dead_code)] from compose.rs and output.rs
- Add explanatory comment for dead_code allow in environment.rs
(needed for API deserialization struct fields)
* dev: rename develop to dev, add clean subcommand
- rename command from `develop` to `dev` (with `develop` alias)
- extract `--clean` flag from `down` into separate `clean` subcommand
- update output: remove "Using port 443" msg, add checkmark to "Started X image services"
* dev: store data in ~/.railway/develop/{projectId} instead of environmentId
* remove unnecessary comments
* dev: add unit tests for compose, https_proxy, local_config
Tests for port building, caddyfile generation, cert existence, and config operations.
* dev: fix cross-platform compatibility for Linux/Windows
- Add extra_hosts to Caddy service for host.docker.internal on Linux
- Use shell execution (sh -c / cmd /C) for proper command parsing
- Replace path unwraps with to_string_lossy()
* dev: check docker compose availability before running
* fix: handle Windows error kind for file lock conflicts
* dev configure: add service menu and --remove <name> support
* dev: clarify comments in up_command
* fix: use fs2 lock_contended_error for cross-platform lock check
* fix: use privateNetworkEndpoint for private domain resolution
Previously used slugified service name which may not match the actual
private network endpoint configured in the environment.
* refactor: extract build_service_endpoints helper for privateNetworkEndpoint
DRYs up duplicate logic between dev.rs and local_override.rs.
Fixes run command using environment_id instead of project_id for
is_local_develop_active check.
* dev: improve empty state messages
Distinguish "no services at all" from "no code services" so users
get actionable guidance (railway add vs railway develop configure)
* fix: only show networking for code services when port configured
* fix: skip canonicalize on windows to avoid UNC path prefix
canonicalize() returns \\?\C:\... paths on Windows which cmd.exe rejects
* dev: improve empty state messages with color
* dev: show command as first log line for code services
* dev: add port selection to configure and detect port conflicts
- prompt for port during service configuration (default: inferred from Railway)
- skip port prompt if service has no networking config
- detect and warn about port conflicts during configure
- force reconfigure conflicting ports during `railway dev up`
- only set PORT env var for services with networking configured
* dev: allow configuring multiple services on first-time setup
Use multi-select prompt instead of single-select, with summary at end
* dev: show OS-specific Docker install URL
* dev: format Docker not found message
* remove test dir
* remove test and to gitignore
* dev: fix cross-service public domain replacement
When service A references service B's RAILWAY_PUBLIC_DOMAIN via variable
interpolation, the production domain wasn't being replaced with the local
equivalent. Now builds a mapping of production -> local public domains
and replaces them in all variable values.
* dev: show next steps when only image services running
* dev: extract constants and refactor up_command
- Add port range constants (PORT_RANGE_MIN, PORT_RANGE_SIZE, RANDOM_PORT_MIN, RANDOM_PORT_MAX)
- Add resolve_path() for cross-platform path canonicalization
- Move generate_random_port() to ports.rs
- Extract helper functions from up_command:
- print_image_service_summary()
- print_code_service_summary()
- build_public_domain_mapping()
- build_image_service_compose()
- setup_caddy_proxy()
* Properly show external workspaces
We are going away with the concept of a personal workspace. Right now personal workspaces in the CLI have both projects the user is a member of and projects owned by the workspace created for the user when they signed up.
And that workspace created for the user when they signup now has a team, so it appears twice in the selector.
To solve this we stop using `me.projects` and go throught he externalWorkspaces query, displaying each external project in its workspace.
* Cleanup workspace fetching
* Improve messaging when using the deprecated flag
* feat: add check updates command
* feat: run check_update on required commands
* review: added return value to check_updates
simplified check_updates command
fix: more user-friendly error message for check_updates on Config write
failure
* refactor: check updates macro -> function
* lint: cargo fmt
* review: remove extra clap comment
* feat: check for updates in background and print on next run
feat: check for updates in background and print on next run
remove updates check from init, link and run
move functions around in main.rs
* Add check to see if the cli has been updated
* fix: CI / Tests (Windows)
* fix: match try_parse() error handling behaviour with parse()
* nit: move tests below impl
* fix: compare_semver: handle different length versions
* chore: remove big comment
---------
Co-authored-by: Jake Runzer <jakerunzer@gmail.com>
* added ssh command
* less verbose error msg
* use linked project and service for ssh command
* pass environment id to ssh websocket connection
* x-railway-deployment-instance-id header
* fix wording when selecting a service
* ensure we reset terminal after ssh session
* remove useless terminal check
* fix windows build
---------
Co-authored-by: Miguel Casqueira <20k-ultra@users.noreply.github.com>
Co-authored-by: Jake Runzer <jakerunzer@gmail.com>
* wrap graphql errors with custom railway errors
* get project controller function and error handling
* s/RailwayProject/LinkedProject
* better errors in link command
* use get_project everywhere
* unified fetching of matched environment
* use new graphql function everywhere
* better error when running link with no projects
* lint
* dont error if the user cancels a prompt
* remove aborted by user message
* move more const errors to RailwayError's
* move no service linked error to railway error
* better message when only a single environment
* format
* better error if railway up fails
* better no linked project error
* Reorder the structs of mutations alphabetically
* Add documentation comment for `interact_or!` macro and make Clippy happy
* Implement `down` subcommand
* delete everything
* migrate to v3
* basic logout command
* remove pre-release text
* additional error handling in rlwy domain
* fixed backwards logic on rlwy domain
* accept environment id in rlwy run
* default environment on rlwy link if there's only 1
* dont print plugins and services if there are none
* skip serializing nones
* remove entities file
* rename ServiceDomains
* link to project upon init
* remove description prompt
* fully remove description prompt
* remove dead code
* refactor some of the commands
* update repo target
* fix broken render_config
* cleanup unused vars
* remove muts
* bump cargo version to 3.0.0
* change repo on Cargo.toml
---------
Co-authored-by: Angelo <asara019@fiu.edu>