Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .changeset/staging-host-secrets.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
"everything-dev": minor
---

Add `bos deploy` command, host secrets, and staging environment support

- **New `bos deploy` command**: Publishes config to FastKV and triggers Railway redeploy in one step. Reads service name from `ci.railway.service` in `bos.config.json`. Uses `RAILWAY_TOKEN` (environment-scoped) instead of deployment IDs.

- **New `ci` config section**: `bos.config.json` now accepts `ci.railway.service` for Railway integration. Child projects inherit this via extends.

- **Staging environment support**: `BOS_ENV=staging` or `--env staging` enables staging mode. `staging.domain` overrides `domain`, FastKV publishes under the staging gateway key, and runtime sets `env = "staging"`.

- **Host secrets**: Added `secrets` array to `app.host` for tenant-related environment variables (`TENANT_WHITELIST`, `ALLOW_OVERRIDE`, `ALLOW_UNTRUSTED_SSR`, `CSP_STRICT`). Validated during `bos start` and surfaced in `bos infra`.

- **Workflow simplification**: Replaced `publish.yml` with `deploy.yml`. `release.yml` now only handles npm package releases. `staging.yml` uses `bos deploy --env staging`. All workflows use `railway redeploy` via CLI instead of raw GraphQL API calls.

- **Removed**: `RAILWAY_PRODUCTION_SERVICE_ID` and `RAILWAY_STAGING_SERVICE_ID` variables — replaced by environment-scoped `RAILWAY_TOKEN` secrets.
10 changes: 10 additions & 0 deletions .changeset/tenant-ssr-integrity-gate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
"everything-dev": patch
"host": patch
---

Require ssrIntegrity for tenant SSR — prevent no-cache-per-request MF instance creation

Tenant SSR now requires both `ssrUrl` and `ssrIntegrity` to be present. Previously, a whitelisted tenant with `ssrUrl` but no `ssrIntegrity` would bypass the router module cache (`shouldCacheRouterModule` returns false without `ssrIntegrity`), causing a new Module Federation instance to be created on every SSR request — the same pattern that caused the production SSR failure.

Also fixes pre-existing typecheck errors in host test files (Effect Either narrowing, FederationError type annotation).
4 changes: 4 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@

# app.host
CORS_ORIGIN=http://localhost:3000
TENANT_WHITELIST=
ALLOW_OVERRIDE=
ALLOW_UNTRUSTED_SSR=
CSP_STRICT=

# app.api
API_DATABASE_URL=postgres://everythingdev:everythingdev@localhost:5432/api_db
Expand Down
62 changes: 62 additions & 0 deletions .github/templates/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
name: Deploy

on:
workflow_run:
workflows: [CI]
types: [completed]
workflow_dispatch:

concurrency: ${{ github.workflow }}-${{ github.ref }}

permissions:
contents: write

jobs:
deploy:
name: Deploy Production
if: >
github.event_name == 'workflow_dispatch' ||
(
github.event_name == 'workflow_run' &&
github.event.workflow_run.conclusion == 'success' &&
github.event.workflow_run.event == 'push' &&
github.event.workflow_run.head_branch == 'main'
)
runs-on: ubuntu-latest
env:
BOS_INSTALL_NEAR_CLI: "true"
NEAR_PRIVATE_KEY: ${{ secrets.NEAR_PRIVATE_KEY }}
ZE_SERVER_TOKEN: ${{ secrets.ZEPHYR_AUTH_TOKEN }}
ZE_USER_EMAIL: ${{ secrets.ZEPHYR_USER_EMAIL }}
RAILWAY_TOKEN: ${{ secrets.RAILWAY_TOKEN }}
steps:
- name: Checkout code
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1

- name: Setup Bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version: "1.2.20"

- name: Install dependencies
run: bun install --frozen-lockfile --ignore-scripts

- name: Build every-plugin
run: bun run --cwd packages/every-plugin build

- name: Run postinstall
run: bun run postinstall

- name: Install Railway CLI
run: npm i -g @railway/cli

- name: Deploy
run: bos deploy

- name: Commit and push bos.config.json updates
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add bos.config.json
git diff --cached --quiet || git commit -m "chore: update deployment URLs [skip ci]"
git push
56 changes: 0 additions & 56 deletions .github/templates/workflows/publish.yml

This file was deleted.

56 changes: 56 additions & 0 deletions .github/templates/workflows/staging.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
name: Staging

on:
push:
branches:
- staging
workflow_dispatch:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: write

jobs:
deploy-staging:
name: Deploy Staging
runs-on: ubuntu-latest
env:
BOS_INSTALL_NEAR_CLI: "true"
NEAR_PRIVATE_KEY: ${{ secrets.NEAR_PRIVATE_KEY }}
ZE_SERVER_TOKEN: ${{ secrets.ZEPHYR_AUTH_TOKEN }}
ZE_USER_EMAIL: ${{ secrets.ZEPHYR_USER_EMAIL }}
RAILWAY_TOKEN: ${{ secrets.RAILWAY_STAGING_TOKEN }}
steps:
- name: Checkout code
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1

- name: Setup Bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version: "1.2.20"

- name: Install dependencies
run: bun install --frozen-lockfile --ignore-scripts

- name: Build every-plugin
run: bun run --cwd packages/every-plugin build

- name: Run postinstall
run: bun run postinstall

- name: Install Railway CLI
run: npm i -g @railway/cli

- name: Deploy
run: bos deploy --env staging

- name: Commit and push bos.config.json updates
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add bos.config.json
git diff --cached --quiet || git commit -m "chore: update staging deployment URLs [skip ci]"
git push
13 changes: 1 addition & 12 deletions .github/workflows/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ This repository uses the following production-facing workflows:
- `Docker` — Docker build and push after successful `CI` on `main`
- `Release` — changeset versioning and npm publish for framework packages
- `Publish` — app deploy (Zephyr CDN + FastKV config publish)
- `Preview` — PR preview comments via Railway

The key design: `CI` is the validation workflow. `Release`, `Publish`, and `Docker` run as standalone workflows after successful `CI` runs on `main` via `workflow_run`. Docker remains decoupled so long image builds do not delay release or publish.

Expand Down Expand Up @@ -74,16 +73,6 @@ The key design: `CI` is the validation workflow. `Release`, `Publish`, and `Dock

**Secrets:** `NEAR_PRIVATE_KEY`, `ZEPHYR_AUTH_TOKEN`, and `ZEPHYR_USER_EMAIL` come directly from repository secrets. NEAR for FastKV publish, Zephyr for CDN deploy.

### Preview (`preview.yml`)

**Trigger:** `pull_request` close events for cleanup, plus `workflow_run` after successful PR CI.

**Purpose:** Publish the Railway preview URL as a PR comment.

**Security:** Uses `workflow_run` only after successful internal PR CI, so repository secrets are not exposed to forked PRs.

**Configuration:** Set `RAILWAY_TOKEN` and `RAILWAY_PROJECT_ID` as GitHub Actions secrets. Optionally set `RAILWAY_SERVICE_NAME` as a repository variable.

## Docker Image Architecture

Docker images are built in `docker.yml`. The image uses a multi-stage build:
Expand Down Expand Up @@ -129,7 +118,7 @@ npm packages are published using **Trusted Publishing** (OpenID Connect), which
| Variable | Where | Purpose |
|----------|-------|---------|
| `NEAR_PRIVATE_KEY` | Publish | NEAR key for FastKV config publish |
| `ZEPHYR_AUTH_TOKEN` | Publish (as `ZE_SECRET_TOKEN`) | Zephyr Cloud auth for CDN deploy |
| `ZEPHYR_AUTH_TOKEN` | Publish (as `ZE_SERVER_TOKEN`) | Zephyr Cloud auth for CDN deploy |
| `ZEPHYR_USER_EMAIL` | Publish (as `ZE_USER_EMAIL`) | Zephyr Cloud user email |
| `BOS_INSTALL_NEAR_CLI` | Release, Publish | Ensures NEAR CLI is available |
| `GITHUB_TOKEN` | Release, Publish | Changesets PR creation, GitHub releases |
62 changes: 62 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
name: Deploy

on:
workflow_run:
workflows: [CI]
types: [completed]
workflow_dispatch:

concurrency: ${{ github.workflow }}-${{ github.ref }}

permissions:
contents: write

jobs:
deploy:
name: Deploy Production
if: >
github.event_name == 'workflow_dispatch' ||
(
github.event_name == 'workflow_run' &&
github.event.workflow_run.conclusion == 'success' &&
github.event.workflow_run.event == 'push' &&
github.event.workflow_run.head_branch == 'main'
)
runs-on: ubuntu-latest
env:
BOS_INSTALL_NEAR_CLI: "true"
NEAR_PRIVATE_KEY: ${{ secrets.NEAR_PRIVATE_KEY }}
ZE_SERVER_TOKEN: ${{ secrets.ZEPHYR_AUTH_TOKEN }}
ZE_USER_EMAIL: ${{ secrets.ZEPHYR_USER_EMAIL }}
RAILWAY_TOKEN: ${{ secrets.RAILWAY_TOKEN }}
steps:
- name: Checkout code
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1

- name: Setup Bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version: "1.2.20"

- name: Install dependencies
run: bun install --frozen-lockfile --ignore-scripts

- name: Build every-plugin
run: bun run --cwd packages/every-plugin build

- name: Run postinstall
run: bun run postinstall

- name: Install Railway CLI
run: npm i -g @railway/cli

- name: Deploy
run: bun packages/everything-dev/src/cli.ts deploy

- name: Commit and push bos.config.json updates
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add bos.config.json
git diff --cached --quiet || git commit -m "chore: update deployment URLs [skip ci]"
git push
3 changes: 2 additions & 1 deletion .github/workflows/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
github.event_name == 'workflow_run' &&
github.event.workflow_run.conclusion == 'success' &&
github.event.workflow_run.event == 'push' &&
github.event.workflow_run.head_branch == 'main'
(github.event.workflow_run.head_branch == 'main' || github.event.workflow_run.head_branch == 'staging')
)
steps:
- name: Checkout code
Expand Down Expand Up @@ -65,6 +65,7 @@ jobs:
images: ghcr.io/${{ env.REPO }}
tags: |
type=raw,value=latest,enable={{is_default_branch}}
type=raw,value=staging,enable=${{ github.ref == 'refs/heads/staging' }}
type=ref,event=branch
type=sha

Expand Down
Loading
Loading