## Related issue Follow-up to #4673. ## Summary - Add a typed, default-off release-feature registry driven by one comma-separated `OMNIGENT_FEATURES` environment variable, with strict validation and lifecycle metadata. - Gate the web Usage route/navigation and page-only report enrichment while preserving the existing `GET /v1/usage` CLI API. - Migrate web-driven harness installation to the same immutable startup snapshot and wire rollout configuration across Docker, Kubernetes, Render, Railway, and Databricks. ELI5: the server reads one list of enabled features when it starts, enforces that same list on backend routes, and tells the web app which controls and pages to show. ```text OMNIGENT_FEATURES | v FeatureFlags snapshot / \ backend gates GET /v1/info | v frontend gates ``` ## Test Plan - `uv run pytest tests/server/test_feature_flags.py tests/host/test_local_server.py tests/server/integration/test_utility_endpoints.py tests/server/integration/test_hosts_install_harness.py tests/server/integration/test_hosts_store_credential.py tests/server/routes/test_usage_report.py tests/server/test_openapi_drift.py -q` - `cd web && pnpm vitest run src/lib/capabilities.test.ts src/lib/harnessSetup.test.ts src/App.test.tsx src/shell/Sidebar.test.tsx` - `uv run pytest tests/e2e_ui/sessions/test_usage_page_feature.py -q` - `uv run python scripts/dump_openapi.py --check` - `pre-commit run --files <changed files>` - Verified default-off and enabled Usage route/sidebar behavior, strict unknown-feature rejection, legacy CLI usage compatibility, and harness route enforcement. ## Demo - Default off: the updated visual baselines show the original sidebar without the Usage row. - Enabled Usage page: https://github.com/user-attachments/assets/8385d4f0-47ad-430f-bf2c-06c35af6c499 ## Type of change - [ ] Bug fix - [x] Feature - [x] UI / frontend change - [ ] Refactor / chore - [x] Docs - [ ] Test / CI - [ ] Breaking change ## Test coverage - [x] Unit tests added / updated - [x] Integration tests added / updated - [x] E2E tests added / updated - [x] Manual verification completed - [x] Existing tests cover this change - [ ] Not applicable ## Coverage notes Manually reviewed the default-off visual output and verified that the Usage route is absent while the capability is disabled. Targeted backend and frontend tests cover both flag states, capability parsing, startup snapshots, and harness enforcement. ## Changelog Usage and web-driven harness setup can now be enabled per deployment with `OMNIGENT_FEATURES`. Signed-off-by: Zeyi (Rice) Fan <zeyi.f@databricks.com>
7.7 KiB
Omnigent on Railway
Deploy Omnigent to Railway. Railway pulls the pre-built image, runs it next to
a managed Postgres, and serves it over HTTPS on *.up.railway.app.
Railway is not yet a true one-click. Unlike Render's
render.yaml(fully declarative — Postgres, port, and env all wired automatically), a barerailway.tomlleaves several things to wire by hand (steps below). A real one-click experience needs a published Railway template that pre-wires the Postgres reference,HOST, and target port — tracked as a follow-up. Until then, use the manual steps here. (Render is the smoother path today.)
What gets provisioned
- omnigent — web service that pulls
ghcr.io/omnigent-ai/omnigent-serverviadeploy/docker/Dockerfile.prebuilt, served onhttps://<project>.up.railway.app. - Postgres — Railway-managed PostgreSQL plugin you add to the project.
Railway links its
DATABASE_URLinto the app as a reference to the database instance's variable (largely automatic), but the value can lag on the first deploy — see step 2.
Artifact storage uses the container's local filesystem by default (ephemeral
across redeploys). For persistence, add a Railway Volume mounted at
/data/artifacts.
Optional: external Neon Postgres. Instead of the Railway plugin, you can point
DATABASE_URLat a Neon database (pg.new) — e.g. for Neon's serverless scale-to-zero or branching. Tradeoff: you lose the integrated provisioning (a separate signup + connection string) and add some cross-provider latency, so the Railway plugin stays the simpler default.
Setup (built-in accounts — the default)
Defaults to the accounts auth provider: multi-user, no external IdP. The
steps below are validated end-to-end:
- Deploy from the repo — New Project → Deploy from GitHub repo → this repo.
Railway reads
railway.tomland pulls the image. Add a Postgres plugin to the project. - Database — Railway links the Postgres
DATABASE_URLinto the app as a reference to the db instance's variable (largely automatic when you add the plugin). If the first deploy errors withDATABASE_URL is required, the reference value simply hadn't propagated yet — redeploy and it resolves. (To confirm, the app service should have aDATABASE_URLvariable referencing the Postgres service, e.g.${{Postgres.DATABASE_URL}}.) - Create the first admin. No credentials are auto-generated. The
first-boot Deploy logs print a "No admin yet" line pointing at your
*.up.railway.appURL (printed once; idempotent — later boots don't reprint). Open that URL and use the web Create-admin form to pick your own username + password. - Log in with the admin you just created, invite teammates from Members.
HOSTis handled automatically. Railway injectsHOST=[::], which a socket bind can't use and which Railway's IPv4 edge can't reach; the entrypoint detects Railway and coerces it to0.0.0.0, so no manualHOSTvariable is needed. If the generated domain returns "Application failed to respond," Railway's port auto-detect picked the wrong port — open Settings → Networking and set the domain's target port to thePORTRailway injected (shown in the boot log asUvicorn running on …:<port>).
The cookie secret is auto-minted and
OMNIGENT_ACCOUNTS_BASE_URLis auto-detected fromRAILWAY_PUBLIC_DOMAIN, so those don't need setting. To pin a known admin password, setOMNIGENT_ACCOUNTS_INIT_ADMIN_PASSWORDbefore first boot.
Security note for public deployments:
POST /auth/setupis unauthenticated while no password-bearing account exists, so an instance exposed before you reach the Create-admin form can be claimed by the first visitor. Pre-seedOMNIGENT_ACCOUNTS_INIT_ADMIN_PASSWORD, or complete setup promptly after the deploy goes live.
Release features
In the Omnigent service's Variables tab, set OMNIGENT_FEATURES to a
comma-separated enabled set such as usage_page. Railway redeploys the service
automatically. Remove the key from the value to roll back, then reload the web
app. See designs/FEATURE_FLAGS.md for known
keys.
Use your own IdP instead (OIDC)
Prefer GitHub / Google / Okta login over built-in accounts? Switch the provider
in the service Variables. OIDC requires HTTPS — Railway provides it
automatically on *.up.railway.app. If you set a custom domain, point it at
your project before completing these steps.
GitHub OAuth (simplest to register)
-
Go to
github.com/settings/developers→ New OAuth App.- Homepage URL:
https://<project>.up.railway.app - Authorization callback URL:
https://<project>.up.railway.app/auth/callback - Click Register application, then Generate a new client secret.
- Homepage URL:
-
In your Railway project, open the omnigent service → Variables and add:
Variable Value OMNIGENT_AUTH_PROVIDERoidcOMNIGENT_OIDC_ISSUERhttps://github.comOMNIGENT_OIDC_CLIENT_IDyour GitHub OAuth client ID OMNIGENT_OIDC_CLIENT_SECRETyour GitHub OAuth client secret OMNIGENT_OIDC_REDIRECT_URIhttps://<project>.up.railway.app/auth/callbackOMNIGENT_OIDC_COOKIE_SECREToutput of openssl rand -hex 32 -
Railway redeploys automatically. Visit the URL — you'll be redirected to GitHub to log in.
Google Workspace
| Variable | Value |
|---|---|
OMNIGENT_AUTH_PROVIDER |
oidc |
OMNIGENT_OIDC_ISSUER |
https://accounts.google.com |
OMNIGENT_OIDC_CLIENT_ID |
…apps.googleusercontent.com |
OMNIGENT_OIDC_CLIENT_SECRET |
your client secret |
OMNIGENT_OIDC_REDIRECT_URI |
https://<project>.up.railway.app/auth/callback |
OMNIGENT_OIDC_COOKIE_SECRET |
output of openssl rand -hex 32 |
OMNIGENT_OIDC_ALLOWED_DOMAINS |
example.com (critical — see note below) |
Important: Without
OMNIGENT_OIDC_ALLOWED_DOMAINS, any Google account can log in when the OAuth consent screen is "External." Always restrict to your domain.
Generic OIDC (Okta, Auth0, Keycloak, Entra ID)
Set OMNIGENT_OIDC_ISSUER to your IdP's base URL (the one that publishes
/.well-known/openid-configuration). The rest of the variables are the same
as above.
Custom domain
In your Railway project, open Settings → Domains → Add domain. Point your DNS A/AAAA record at the Railway-assigned address. Railway provisions a Let's Encrypt cert automatically.
Update OMNIGENT_OIDC_REDIRECT_URI to use the custom domain after DNS
propagates.
Upgrading
Railway redeploys automatically when a new image tag is pushed to GHCR (if you've configured a webhook) or on demand:
- In the Railway dashboard, open the omnigent service.
- Click Deploy → Latest to pull the newest
:latestimage.
Cost
Railway Hobby plan: ~$5/month base + per-minute CPU/memory usage. A lightly loaded Omnigent instance (few concurrent users) typically stays under $10–15/month total including the Postgres plugin.
Publishing the template
One-time setup done by the repo owner after the repository is public:
- Go to
railway.com/new/templateand click Create template. - Point it at
github.com/omnigent-ai/omnigent. - Select the Postgres plugin.
- Pre-fill default env vars with descriptions for the optional OIDC fields.
- Click Publish. Copy the generated deploy URL and update the badge at the
top of this file and in
deploy/README.md.