# Railway Hosting Primary public hosting option for Cohesivity tenants. Cohesivity creates and owns the Railway project/service/environment/domain, and tenant agents deploy by uploading source files directly to Cohesivity. No GitHub repo, Railway CLI install, or tenant Railway credentials are required. ## Prerequisites Edge calls require this resource to be provisioned first; unprovisioned calls return an error. ### Provision curl -s -X POST https://cohesivity.ai/api/resources/railway-hosting \ -H "Authorization: Bearer " ### Delete curl -s -X DELETE https://cohesivity.ai/api/resources/railway-hosting \ -H "Authorization: Bearer " Provisioning happens once, before the application runs; the running application does not provision its own resources. ## What Happens on Provision - Creates a Railway project `coh-` under Cohesivity-owned Railway credentials - Creates an empty `web` service and `production` environment - Attaches an existing tenant vanity, or otherwise the automatic `.cohesivity.app` host, and returns it as the single `deployment_url` - Routes that Cohesivity-managed hostname through the shared Cloudflare ingress to Railway's internal generated HTTPS origin immediately, so managed hosts do not wait for a per-host Railway certificate - Applies Cohesivity tier compute caps to the Railway service instance and stores the applied cap/status metadata in Cohesivity - Stores Railway project, service, environment, domain, compute cap, and last deployment metadata in Cohesivity The provisioning response is intentionally small: `success`, `resource`, `status`, `deployment_url`, `compute_limits`, `already_provisioned`, `deploy_endpoint`, and `docs_url`. It does not return provider ids, provider domains, generated scripts, or repeated next-step prose. ## Compute Caps Railway topology and CPU/RAM/replica/sleep limits are Cohesivity-managed and not tenant-callable in v1. The service is always `web` and the environment is always `production`; bodies containing `service_name` or `environment_name` return `400 railway_topology_managed`. Re-provisioning `railway-hosting` idempotently reapplies the active tier cap, and claiming an ephemeral tenant reconciles its Railway cap to the claimed account tier in the background. - Ephemeral: 0.5 vCPU, 1 GB RAM, 1 replica, sleep enabled - Claimed Free: 0.5 vCPU, 1 GB RAM, 1 replica, sleep enabled - Plus: 1 vCPU, 2 GB RAM, 1 replica, sleep disabled - Pro: 2 vCPU, 4 GB RAM, 1 replica, sleep disabled ## Runtime metering A minute-level poller reads Railway `usage` for the UTC month (`CPU_USAGE`, `MEMORY_USAGE_GB`, `NETWORK_TX_GB`) and records deltas as `vcpu_seconds`, `memory_gb_seconds`, and `egress_bytes`. Railway reports CPU and memory in its per-minute billing units, which Cohesivity normalizes to seconds on ingest; egress is a volume and is converted from GB to bytes. Claimed overage auto-purchases fluid blocks at Railway's published rates plus 10%. When fluid is exhausted the offering pauses; the poller then sets `sleepApplication: true`, and after a grace window (immediate for Ephemeral/Free, 24h for Plus/Pro) stops the running deployment. Stopping halts compute billing without destroying anything — the service, its source, variables, domains, and TLS certificate all survive. Hosting is never deleted for non-payment. Topup or a new monthly grant redeploys the stopped deployment, restores tier compute caps, and resumes the offering. Metering starts when a project first becomes observable to the poller: a project provisioned mid-month is metered from provisioning onward, and consumption that predates metering is never billed retroactively. ## How to Deploy POST `multipart/form-data` to `/api/railway/deploy`: each project file is a `files` part. The endpoint validates paths/count/size, creates the gzip tarball Railway expects, and uploads it to Railway server-side with Cohesivity-owned auth. Append `?wait=ready` to wait up to 20 seconds for Railway to report SUCCESS and for the application root on Railway's private generated host to return HTTP 200-399 through a short stability window. Cohesivity then probes the public `deployment_url` once. The wait response separates Railway's `deployment_state`, `application_state`, and `application_http_status` from `public_state`, `public_http_status`, and `public_ready`; Railway provider URLs and dashboard links remain internal. New Cohesivity-managed deployment URLs terminate TLS at the shared edge and should become public as soon as the generated Railway application origin is ready. Historical certificate-bound hosts may still return HTTP 525 or 526 as `public_state: "tls_pending"` until they are migrated. A provider or application wait that uses the 20-second budget returns HTTP 202 with `wait_timed_out: true`. In either case, wait `retry_after_seconds`, then GET the supplied `poll_url`, which is the deployment detail route with `?check=ready`. A public HTTP 404 is `public_state: "unhealthy"`, not success: fix the application root, verify it locally, and redeploy. ### curl ``` curl -F "files=@package.json" \ -F "files=@src/index.js" \ -H "Authorization: Bearer " \ https://cohesivity.ai/api/railway/deploy?wait=ready ``` ### JSON shape ``` POST https://cohesivity.ai/api/railway/deploy?wait=ready Authorization: Bearer Content-Type: application/json { "files": [ { "file": "package.json", "data": "{...}" }, { "file": "src/index.js", "data": "console.log(process.env.PORT)" }, { "file": "public/logo.png", "data": "", "encoding": "base64" } ] } ``` ### Response ```json { "success": true, "deployment_id": "", "state": "SUCCESS", "deployment_state": "SUCCESS", "application_state": "ready", "application_http_status": 200, "deployment_url": "https://.cohesivity.app", "logs_url": "https://cohesivity.ai/api/railway/deployments//logs", "wait_timed_out": false, "readiness_state": "ready", "public_state": "ready", "public_ready": true, "public_http_status": 200, "poll_url": "https://cohesivity.ai/api/railway/deployments/?check=ready" } ``` ## Deployment Management All endpoints require `Authorization: Bearer `. - **List deployments:** `GET https://cohesivity.ai/api/railway/deployments` returns deployment ids, one `state`, the Cohesivity `deployment_url`, the Cohesivity `logs_url`, and timestamps - **Deployment details:** `GET https://cohesivity.ai/api/railway/deployments/:id` returns the same public fields plus a curated `last_error` for terminal failures. Append `?check=ready` for one non-blocking public probe that adds `public_state`, `public_ready`, and `public_http_status` - **Deployment logs:** `GET https://cohesivity.ai/api/railway/deployments/:id/logs?type=build|runtime|http` (Cohesivity proxies Railway build, deployment, and HTTP log queries; build/runtime severity is normalized from message prefixes, with a differing raw value preserved as `provider_severity`) - **Domain/status detail:** `GET https://cohesivity.ai/api/railway/domain` or `GET https://cohesivity.ai/api/railway/status` ## Environment Variables Set env vars before deploying when your build reads them. Railway does not return decrypted values on list, so list responses include names/metadata only. The account env manager uses Railway for `railway-hosting` tenants. - **List env vars:** `GET https://cohesivity.ai/api/railway/env` - **Upsert env var:** `POST https://cohesivity.ai/api/railway/env` with `{ "key": "MY_SECRET", "value": "abc123" }`. Values are limited to 32,768 UTF-8 bytes. Writes always use `skip_deploys: true`; `skip_deploys: false` returns `400 provider_deploy_not_allowed`, and callers must use the explicit deploy endpoint. - **Delete env var:** `DELETE https://cohesivity.ai/api/railway/env/:key`; a missing key returns `404 env_var_not_found`. Environment responses expose variable names, sealed/write-only state, update timestamps, and create-time fingerprints only. They never include Railway variable or service ids. ## Tenant Vanity (claimed tenants) A tenant may claim one immutable vanity such as `acme`. Hosting uses `acme.cohesivity.app`; an active Inbox uses `acme@inbox.cohesivity.app`. Whichever offering is provisioned second inherits the first offering's existing claim automatically. New Cohesivity-managed vanity hosts use one proxied Cloudflare CNAME and shared edge TLS, so they need no Railway custom-domain certificate or ownership TXT; tenants never provide provider tokens, and failed DNS writes do not commit the vanity hosting binding. - **Check availability** (no auth): `GET https://cohesivity.ai/api/vanity/check?name=acme`. - **Get current** (auth): `GET https://cohesivity.ai/api/vanity`. New Railway-backed Cohesivity vanities report `verified: true` after their proxied edge route is installed; the shared wildcard certificate already covers the host. Historical Railway custom-domain bindings continue to refresh provider certificate status until migrated. - **Claim once** (auth): `POST https://cohesivity.ai/api/vanity` with `{ "name": "acme" }`. For Railway hosting, `https://acme.cohesivity.app` becomes the hosting `deployment_url` immediately; no re-provision is required. Reposting `acme` is idempotent. Posting a different name returns `409 vanity_immutable`. - **No release or transfer:** `DELETE https://cohesivity.ai/api/vanity` returns `409 vanity_immutable`. Account deletion leaves a permanent tombstone, so the identity is never reassigned. Legacy `/api/railway/custom-subdomain` remains a compatibility alias for the shared vanity handlers. ## Custom Domains Claimed Railway tenants can attach their own apex domain with `POST /api/railway/domains`. Cohesivity keeps provider ids and managed-DNS bookkeeping internally in `railway_custom_domains`; tenant responses contain only the domain state and actions the caller can take. - **Cohesivity-purchased domain**: `POST https://cohesivity.ai/api/railway/domains` with `{ "domain_name": "yourapp.com", "source": "cohesivity-purchased", "include_www": true }`. Cohesivity attaches the domain, creates the required Name.com DNS records, and polls readiness. Purchased-domain responses never expose provider ids or managed DNS ids. Omitting `source` keeps this default. - **Bring-your-own-domain (BYOD)**: `POST https://cohesivity.ai/api/railway/domains` with `{ "domain_name": "yourapp.com", "source": "byod", "include_www": true }`. Cohesivity returns BYOD `records_to_add` containing only `host`, `type`, `value`, and `ttl`. Add every routing record and every ownership TXT record at the external registrar. The required CNAME value may name Railway because it is DNS configuration, never an app or dashboard URL. - **Status**: `GET https://cohesivity.ai/api/railway/domains/yourapp.com` refreshes current readiness. `verified` is true only when TLS certificates are issued for the apex and requested `www` host; issuance proves ownership even if Railway's auxiliary DNS flag remains false behind proxied DNS. - **Detach**: `DELETE https://cohesivity.ai/api/railway/domains/yourapp.com` removes Cohesivity-created Railway custom domains. Purchased domains also remove only tracked Name.com records; BYOD returns `records_to_remove` for the external registrar. Hosting teardown and account deletion perform the same binding cleanup before deleting the Railway project. A binding that references an already-deleted project is reconciled locally instead of remaining a permanent conflict. - `source` is strict (`byod` or `cohesivity-purchased`) and `include_www` must be a JSON boolean; invalid values return 400 instead of being silently coerced. ## Security Tenant agents never send Railway tokens. The only auth they use is Cohesivity `coh_management_key`; Cohesivity injects Railway auth server-side and records only ownership/metadata needed for lifecycle, status, and teardown. Railway service URLs, project links, topology ids, custom-domain ids, DNS bookkeeping ids, and environment-variable ids are excluded from tenant-facing responses. The sole provider hostname a tenant may see is an actionable BYOD CNAME value that must be copied into external DNS. ## Rate Limits Ephemeral tenants pause as a whole if any authoritative hard cap below is exceeded. Claimed tiers use account-scoped buckets shared across every project owned by the Cohesivity user; OpenAI, AI Gateway, Deepgram, and Exa are fluid-only after tier, rate, and concurrency checks; AI Gateway and Deepgram have no fixed monthly usage bucket for claimed tiers. **Ephemeral** - projects: 1 max total - deploys: 20 per ephemeral tenant lifetime before claim or expiry - uploaded bytes: 250 MB per ephemeral tenant lifetime before claim or expiry - vCPU-seconds: 50000 per ephemeral tenant lifetime before claim or expiry - memory GB-seconds: 150000 per ephemeral tenant lifetime before claim or expiry - egress bytes: 2 GB per ephemeral tenant lifetime before claim or expiry - deploys: 3 per minute **Claimed Free** - projects: 10 max total - deploys: 3 per minute - deploys: 60 per month - uploaded bytes: 2 GB per month - vCPU-seconds: 100000 per month - memory GB-seconds: 400000 per month - egress bytes: 5 GB per month **Claimed Plus** - projects: 30 max total - deploys: 10 per minute - deploys: 200 per month - uploaded bytes: 10 GB per month - vCPU-seconds: 400000 per month - memory GB-seconds: 1500000 per month - egress bytes: 50 GB per month **Claimed Pro** - projects: 100 max total - deploys: 10 per minute - deploys: 600 per month - uploaded bytes: 50 GB per month - vCPU-seconds: 1500000 per month - memory GB-seconds: 6000000 per month - egress bytes: 250 GB per month ### Notes - Railway deploys and upload bytes remain Cohesivity-side hard counters. Runtime vCPU-seconds, memory GB-seconds, and egress bytes are polled from Railway each minute into the same counter tables; claimed overage draws from fluid at provider cost plus 10%. When fluid is exhausted the offering pauses and the graduated ladder sleeps then stops the Railway service.