-
Notifications
You must be signed in to change notification settings - Fork 8
Expand file tree
/
Copy pathtemplate.yaml
More file actions
250 lines (241 loc) · 13.7 KB
/
Copy pathtemplate.yaml
File metadata and controls
250 lines (241 loc) · 13.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
# Roomote Railway template definition.
#
# This file is the maintained source of truth for the official Roomote
# templates on Railway's marketplace. Two templates are published from this
# one spec — a develop-channel template tracking `:develop` and a
# main-channel template tracking `:main` — and they differ ONLY in the app
# image alias below; every service, variable, and note is otherwise
# identical. Railway does not consume this file directly: a maintainer
# mirrors it into Railway's Template Composer when creating or updating
# each published template. Values in `${{ ... }}` use Railway's
# reference-variable and template-function syntax and are resolved by
# Railway at deploy time.
#
# Design notes:
# - One shared `roomote-app` image runs web, api, controller, and bullmq;
# each service selects its process through the image entrypoint. Railway's
# custom start command bypasses the entrypoint, so every start command uses
# the full `/roomote/.docker/app/entrypoint.sh <service>` form (and minio
# uses `minio server ...`, not the bare `server ...` compose form).
# - Railway's Template Composer has no environment-level shared variables.
# The api service is the anchor: it defines every shared value once
# (including all `${{secret(n)}}` generated secrets, which MUST NOT be
# pasted into more than one service), and the other app services reference
# them as `${{api.NAME}}`.
# - Each published template tracks one mutable channel alias — `:develop`
# (every develop build) or `:main` (every main build) — and contains no
# version strings. The images bake RELEASE_VERSION; the app derives
# DOCKER_WORKER_IMAGE and MODAL_BASE_IMAGE_REF from it, and the controller
# reads the worker release version from the VERSION file inside
# worker-current.tar.gz. Production deployments can pin an immutable tag
# (v*, develop-<sha>, or main-<sha>) in the five image fields instead;
# nothing else needs editing. Railway does not redeploy when a mutable
# alias moves; redeploys can be automated against Railway's GraphQL API
# (README.md "Auto-deploying every develop build").
# - Railway has no Docker socket, so task execution must use a hosted
# sandbox provider (modal by default; e2b and daytona also work).
# EXCLUDED_COMPUTE_PROVIDERS=docker hides the unusable provider.
# - Zero required deploy-time inputs. Modal tokens and model provider keys
# are entered in the /setup wizard after first boot and stored encrypted
# in Postgres. Auth keypairs come from R_AUTO_GENERATE_KEYS=true.
# The optional deploy-time prompts are R_APP_URL on api (see the annotation
# on that variable), for deployers who already own a domain, and
# R_LICENSE_KEY, for operators provisioning a paid-seat license. Override
# the pre-filled R_APP_URL default with https://<their-domain> before the
# first deploy instead of editing the variable after hitting 403 "Invalid
# origin" errors.
# - The api service must be publicly reachable: hosted sandboxes workers and
# GitHub webhooks call `TRPC_URL` directly.
# - Both published images are public on GHCR: Railway pulls roomote-app and
# hosted-sandbox builders pull roomote-worker anonymously, so no registry
# credentials are needed.
# - R_APP_URL on api is the single canonical-origin knob; the other
# app services reference ${{api.R_APP_URL}}. Note api's own value
# is itself a reference (https://${{web.RAILWAY_PUBLIC_DOMAIN}}), so this
# is a reference to a reference. Verified working on a scratch deploy
# (2026-07-08): the app services receive the resolved URL, not a literal
# ${{...}} string. Re-check it on the scratch-project first-boot run
# after template changes (see "Maintaining the marketplace template" in
# README.md); if resolution ever regresses, fall back to repeating the
# raw https://${{web.RAILWAY_PUBLIC_DOMAIN}} value per service and accept
# the multi-service edit when changing domains. Do NOT flatten the
# ${{api.*}} references to raw values in the Composer — that silently
# breaks the one-variable custom-domain edit. A custom domain is either set
# at deploy time through the R_APP_URL prompt or attached later as
# a one-variable edit; see "Attaching a custom domain" in README.md.
# R_PUBLIC_URL is intentionally not set — the app falls
# back to R_APP_URL wherever it would apply. Railway does not
# update RAILWAY_PUBLIC_DOMAIN when a custom domain is attached, and
# browsing an origin the app was not told about fails auth with 403
# "Invalid origin".
# - SETUP_TOKEN=${{secret(32)}} gates the /setup wizard. The app requires a
# setup token everywhere except local development — a deployment without
# one cannot bootstrap its founding admin at all — so the template must
# generate it. The operator copies the value from the api service's
# Variables tab and pastes it into the wizard's token step (the URL form
# /setup?token=<value> also works).
# - R_PING_BASE_URL points the anonymous-analytics and version-check
# pings at the Roomote ping service (the code default is
# https://ping.roomote.dev). Admins can opt out of analytics in the setup
# wizard or Settings -> Misc; version checks ignore that setting.
# - Live previews ship disabled but ready: the preview-proxy service is in
# the template and boots healthy with the PREVIEW_* variables empty (it
# only hard-requires R_APP_URL, DATABASE_URL, and REDIS_URL, all in
# the shared block). Enabling previews needs a customer-owned wildcard
# custom domain plus the three PREVIEW_* values on api; see "Enabling
# live previews" in README.md. The PREVIEW_* variables on api are empty
# and marked OPTIONAL in the Template Composer so the deploy screen does
# not demand values for them.
# The ONLY line that differs between the two published templates. Use the
# alias for the channel being mirrored; deployments can also pin an
# immutable v* / develop-<sha> / main-<sha> tag instead.
image:
app: ghcr.io/roocodeinc/roomote-app:main # main-channel template (stable)
# app: ghcr.io/roocodeinc/roomote-app:develop # develop-channel template
worker: derived # ghcr.io/roocodeinc/roomote-worker:<baked RELEASE_VERSION>, resolved by the app
# The api service anchors every shared value. Other app services reference
# them with `${{api.NAME}}`.
services:
Postgres:
source: Railway managed PostgreSQL
notes: Provides `${{Postgres.DATABASE_URL}}`. Prefer the private-network URL.
Redis:
source: Railway managed Redis
notes: Provides `${{Redis.REDIS_URL}}`. Prefer the private-network URL.
minio:
image: minio/minio:RELEASE.2025-09-07T16-13-09Z@sha256:14cea493d9a34af32f524e538b8346cf79f3321eff8e708c1e2960462bd8936e
service_name: minio # rename from the default "minio/minio" — the slash breaks ${{minio.*}} references
start_command: minio server /data --console-address :9001
volume: /data
public_domain: http proxy port 9000 # S3 API; presigned artifact URLs use this domain
env:
MINIO_ROOT_USER: ${{api.S3_ACCESS_KEY_ID}}
MINIO_ROOT_PASSWORD: ${{api.S3_SECRET_ACCESS_KEY}}
notes: >
The artifact bucket is not auto-created. After the first deploy, create
`roomote-artifacts` once (see "Create the artifact bucket" in
README.md). Replacing MinIO with external S3/R2 is a config-only swap.
api:
image: app
start_command: /roomote/.docker/app/entrypoint.sh api
pre_deploy_command: /roomote/.docker/app/entrypoint.sh db-migrate
public_domain: http proxy port 8080 # hosted workers and GitHub webhooks call this origin
healthcheck_path: /health/liveness
env:
R_APP_ENV: production
ROOMOTE_DOCKER_LOAD_ENV_FILE: 'false'
R_AUTO_GENERATE_KEYS: 'true'
ENCRYPTION_KEY: ${{secret(32)}}
# Transport secret between BullMQ Discord Gateway and API.
R_DISCORD_GATEWAY_SECRET: ${{secret(32)}}
# Optional paid-seat license key; takes precedence over Settings > Users.
R_LICENSE_KEY: ''
R_DISCORD_BOT_TOKEN: '' # optional; Discord can also be configured after boot in Settings
ARTIFACT_SIGNING_KEY: ${{secret(32)}}
DASHBOARD_PASSWORD: ${{secret(24)}}
S3_SECRET_ACCESS_KEY: ${{secret(32)}}
S3_ACCESS_KEY_ID: roomote
S3_REGION: us-east-1
S3_BUCKET_ARTIFACTS: roomote-artifacts
S3_AUTO_CREATE_BUCKET: 'true' # api creates the MinIO bucket at boot; no manual mc step
# Live-preview knobs, empty until previews are enabled (mark all
# three "optional" in the Composer so the deploy screen skips them).
# To enable: set to https://previews.<domain> / https://previews.<domain>
# / previews.<domain> after attaching the wildcard custom domain to
# the preview-proxy service — see "Enabling live previews" in README.md.
PREVIEW_PROXY_BASE_URL: ''
NEXT_PUBLIC_PREVIEW_PROXY_BASE_URL: ''
PREVIEW_DOMAINS: ''
SETUP_TOKEN: ${{secret(32)}} # gates /setup; required outside local dev (see design notes)
R_PING_BASE_URL: https://ping.roomote.dev # anonymous-analytics / version-check pings
R_INSTANCE_ID: '' # optional deterministic Ping instance identity
DEFAULT_COMPUTE_PROVIDER: modal
EXCLUDED_COMPUTE_PROVIDERS: docker
DATABASE_URL: ${{Postgres.DATABASE_URL}}
REDIS_URL: ${{Redis.REDIS_URL}}
# The single canonical-origin knob and one of the template's optional
# deploy-time prompts. In the Template Composer, give this variable the
# description below so Railway surfaces it on the deploy screen with
# the reference default pre-filled. Leaving the default keeps the
# Railway-generated domain; overriding it with https://<domain> sets a
# custom domain before first boot, so the setup wizard derives OAuth
# callback and webhook URLs from the right origin from day one. The
# deployer must still attach the domain to the web service and finish
# DNS after deploy — see "Attaching a custom domain" in README.md.
# Composer description: "The URL users will browse. Leave the default
# to use the Railway-generated domain. To use a custom domain, replace
# this with https://app.example.com — then, after deploying, add that
# domain to the web service, finish DNS setup, and open /setup on the
# custom domain."
R_APP_URL: https://${{web.RAILWAY_PUBLIC_DOMAIN}}
TRPC_URL: https://${{api.RAILWAY_PUBLIC_DOMAIN}}
S3_ENDPOINT: http://${{minio.RAILWAY_PRIVATE_DOMAIN}}:9000
S3_PRESIGN_ENDPOINT: https://${{minio.RAILWAY_PUBLIC_DOMAIN}}
# The env block shared by web, controller, bullmq, and preview-proxy. In
# the Template Composer, paste it into each service (Railway has no
# per-template YAML anchors).
web:
image: app
start_command: /roomote/.docker/app/entrypoint.sh web
public_domain: http proxy port 8080 # this is the app origin users open
healthcheck_path: /health
env: &app_service_env
R_APP_ENV: ${{api.R_APP_ENV}}
ROOMOTE_DOCKER_LOAD_ENV_FILE: ${{api.ROOMOTE_DOCKER_LOAD_ENV_FILE}}
R_AUTO_GENERATE_KEYS: ${{api.R_AUTO_GENERATE_KEYS}}
ENCRYPTION_KEY: ${{api.ENCRYPTION_KEY}}
R_DISCORD_GATEWAY_SECRET: ${{api.R_DISCORD_GATEWAY_SECRET}}
R_LICENSE_KEY: ${{api.R_LICENSE_KEY}}
R_DISCORD_BOT_TOKEN: ${{api.R_DISCORD_BOT_TOKEN}}
ARTIFACT_SIGNING_KEY: ${{api.ARTIFACT_SIGNING_KEY}}
DASHBOARD_PASSWORD: ${{api.DASHBOARD_PASSWORD}}
S3_SECRET_ACCESS_KEY: ${{api.S3_SECRET_ACCESS_KEY}}
S3_ACCESS_KEY_ID: ${{api.S3_ACCESS_KEY_ID}}
S3_REGION: ${{api.S3_REGION}}
S3_BUCKET_ARTIFACTS: ${{api.S3_BUCKET_ARTIFACTS}}
SETUP_TOKEN: ${{api.SETUP_TOKEN}}
R_PING_BASE_URL: ${{api.R_PING_BASE_URL}}
R_INSTANCE_ID: ${{api.R_INSTANCE_ID}}
# Empty until previews are enabled; the web client builds preview
# links from NEXT_PUBLIC_PREVIEW_PROXY_BASE_URL, so the references
# live in the shared block rather than only on preview-proxy.
PREVIEW_PROXY_BASE_URL: ${{api.PREVIEW_PROXY_BASE_URL}}
NEXT_PUBLIC_PREVIEW_PROXY_BASE_URL: ${{api.NEXT_PUBLIC_PREVIEW_PROXY_BASE_URL}}
PREVIEW_DOMAINS: ${{api.PREVIEW_DOMAINS}}
DEFAULT_COMPUTE_PROVIDER: ${{api.DEFAULT_COMPUTE_PROVIDER}}
EXCLUDED_COMPUTE_PROVIDERS: ${{api.EXCLUDED_COMPUTE_PROVIDERS}}
DATABASE_URL: ${{Postgres.DATABASE_URL}}
REDIS_URL: ${{Redis.REDIS_URL}}
R_APP_URL: ${{api.R_APP_URL}}
TRPC_URL: ${{api.TRPC_URL}}
S3_ENDPOINT: ${{api.S3_ENDPOINT}}
S3_PRESIGN_ENDPOINT: ${{api.S3_PRESIGN_ENDPOINT}}
controller:
image: app
start_command: /roomote/.docker/app/entrypoint.sh controller
public_domain: no
env:
<<: *app_service_env
# The worker release archive is baked into the app image; the
# controller uploads it into hosted sandboxes at spawn time.
# The version-less name works because the controller reads the release
# version from the VERSION file inside the archive. Do not unset: the
# fallback is GitHub worker releases, which only exist for tagged v*
# releases, not develop or main branch builds.
DOCKER_WORKER_RELEASE_PATH: /roomote/releases/worker-current.tar.gz
bullmq:
image: app
start_command: /roomote/.docker/app/entrypoint.sh bullmq
public_domain: no
env: *app_service_env
# Boots healthy with the PREVIEW_* variables empty and serves nothing
# until previews are enabled: attach `previews.<your-domain>` and the
# `*.previews.<your-domain>` wildcard as custom domains here, fill the
# three PREVIEW_* values on api, and opt in from Settings -> Live
# Previews. See "Enabling live previews" in README.md.
preview-proxy:
image: app
start_command: /roomote/.docker/app/entrypoint.sh preview-proxy
public_domain: http proxy port 8080 # plus the previews custom domains when enabled
healthcheck_path: /health
env: *app_service_env