everything-dev
Version:
A consolidated product package for building Module Federation apps with oRPC APIs.
266 lines (189 loc) • 14.8 kB
Markdown
---
name: publish-sync
description: Publish the authored config (bos.app.ts) to the FastKV registry, sync from upstream, and upgrade workspace packages. Use when deploying, syncing, or managing runtime configuration across projects.
metadata:
sources: "packages/everything-dev/src/plugin.ts,packages/everything-dev/src/cli/sync.ts,packages/everything-dev/src/cli/upgrade.ts,packages/everything-dev/src/fastkv.ts,packages/everything-dev/src/integrity.ts,packages/everything-dev/src/config.ts"
---
> **Config form:** the authored config is `bos.app.ts` (canonical, preferred when both exist). A legacy `bos.config.json` is still supported for older children. Where this doc says `bos.config.json` for the *local authored file*, read "the authored config". The published artifact on FastKV keeps the key name `bos.config.json`.
# everything-dev Publish & Sync
## Core Workflow
```
Build → Publish → Sync
↓ ↓ ↓
rspack FastKV bos sync
```
## Publish
Publish the resolved config to the configured FastKV registry path for the app account/domain:
```bash
bos publish # Publish config only
bos publish --deploy # Build/deploy all workspaces first, then publish
bos publish --deploy --packages local # Build/deploy only the workspaces owned locally (recommended for CI)
bos publish --dry-run # Preview without sending
bos publish --network testnet
bos publish --packages ui,api # Manual subset
```
`--packages` accepts a comma-separated key list, `all` (default), or `local`. `local` selects only the entries whose `development` field starts with `local:`, so it auto-tracks whatever plugins or apps live in this repo without you editing workflow files when a plugin is added. CI deploy configuration should use `--packages local` exclusively; remote-only plugins can't be redeployed from this repo and will fail with a missing-local-path error.
The registry transaction is signed in-process via near-kit — key resolution order: explicit key → `NEAR_PRIVATE_KEY` / `BOS_NEAR_PRIVATE_KEY` env → `~/.near-credentials/<network>/<account>.json` → near-cli-rs OS keychain (`sign-with-keychain`, interactive only). Publishes are skipped when FastKV already holds an identical config. Reads are indexed by tx signer, so the config resolves at `bos://<account>/<gateway>` only when signed by `<account>` itself — see the `registry` skill for the namespace=signer law and composing other runtimes (`bos registry use`).
After `bos publish --deploy`:
1. Each workspace builds; the publish writes deterministic bundle URLs (`https://<domain>/bundles/<account>/<gateway>/<workspace>/`)
2. Pipeline state (production URLs, integrity) goes to FastKV. For legacy JSON-form projects, `bos.config.json` is also updated locally; for the authored `bos.app.ts` form, nothing is written back — the authoring surface never holds pipeline state
3. Config is published to the FastKV registry at `{account}/bos/gateways/{gateway}/bos.config.json`
The `--network` flag controls the NEAR network (mainnet by default). With `--network testnet`, publishes go to the NEAR testnet chain under the testnet account specified in config.
### Rollback
To revert a publish, restore the previous authored config from git and re-publish:
```bash
git checkout HEAD~1 -- bos.app.ts
bos publish
```
A config-only publish merges the freshly authored config over the currently published one — authored edits win, and the live pipeline state (bundle URLs, integrity) carries over. Removing a slot from the authored config requires `bos deploy`, which regenerates the payload from scratch. Legacy JSON-form children re-publish their `bos.config.json` as-is.
Or cherry-pick a specific version of the authored config and publish that snapshot. Prefer `bos rollback --previous` (or `--version <height>`) — it republishes a verified FastKV snapshot without touching git.
Lineage model:
- `extends` is the canonical parent edge between published runtimes
- `account` is the tenant namespace root for that runtime
- `domain` is the public ingress for that runtime
- a child runtime can extend a parent and still become a new tenant root on its own domain
### Self-Deployed / Tenant Publishing
You don't need to wait for CI/CD to see changes in production. Publish your own config on-chain under your own NEAR account and run your own host instance, inheriting the base platform via `extends`.
**Same gateway, own account:**
`BOS_GATEWAY` (`domain` in the authored config) is the **FastKV lookup key**, not the DNS domain your Railway instance serves on. By keeping `BOS_GATEWAY` the same as the parent while using your own `BOS_ACCOUNT`, your config lives at a separate FastKV path (`bos://<your-account>/<gateway>`) that `extends` the base runtime. You inherit the full platform — host, API, auth, plugins — and override only what you change. Your Railway URL is the ingress.
**Step-by-step:**
1. Install near-cli-rs (v0.23.5) — only needed for account creation and `bos key generate`; `bos publish` signs transactions in-process via near-kit
2. Create a NEAR account via near-cli-rs (testnet or mainnet; named accounts can own subaccounts, implicit hex accounts cannot)
3. `bos key generate` — generates a function-call key scoped to the FastKV registry contract; set the output as `NEAR_PRIVATE_KEY`
4. Update `bos.config.json`: set `account` to your NEAR account, add `"extends": "bos://<parent-account>/<parent-gateway>"`, keep `domain` as the parent gateway
5. `bos publish --deploy` — builds workspaces, writes deterministic bundle URLs, publishes config to FastKV at `bos://<your-account>/<gateway>`
6. Deploy to Railway (one-click template or `railway up`), set `BOS_ACCOUNT`, `BOS_GATEWAY` (same as parent), `BETTER_AUTH_SECRET` — the host fetches your config from FastKV and serves live
**Subaccount creation** (unused by the tenant wizard today — tenants are owned by the connected sputnik-dao account) requires an explicit relayer with a named account: set `siwn.relayer.{mainnet,testnet}.accountId` + a private key, and point `siwn.subAccount.parentAccount`, `siwn.recipients`, and `siwn.relayer.*.whitelistedContracts` to your account.
## Sync
Pull template updates from the parent referenced by the local authored config:
```bash
bos sync
bos sync --force
bos sync --dry-run
```
What gets synced from the parent template:
- `app.*.production` — bundle URLs
- `app.*.ssr` — SSR URLs
- `shared` — shared dependency versions
- framework-owned files like build configs, router wiring, and shared runtime scaffolding
What stays local:
- `account`, `testnet` — your NEAR accounts
- `app.*.development` — local dev URLs
What gets merged:
- `app.*.secrets` — union of remote + local
- `app.*.variables` — merged (local overrides remote)
## Upgrade
Bump `every-plugin` and `everything-dev` across all workspaces:
```bash
bos upgrade # Check for new versions, update, then sync
bos upgrade --dry-run # Preview without making changes
```
`bos upgrade` updates **all workspace `package.json`s**, not just root. Also updates `peerDependencies` and `workspaces.catalog`. Correctly skips `workspace:*` and `catalog:` references.
## Build
```bash
bos build # Build all packages (skips missing)
bos build ui # Build specific package
bos build ui,api # Build multiple
bos build --force # Force rebuild
```
## Config Integrity
`bos.config.json` entries can include `integrity` fields with SRI hashes:
```json
{
"app": {
"ui": {
"production": "https://cdn.example.com/ui/remoteEntry.js",
"integrity": "sha384-abc123..."
}
}
}
```
These are auto-generated during `bos publish --deploy` and verified at runtime by the host.
## Configuration
All runtime config lives in the authored config. Key sections:
- `account` — NEAR mainnet account
- `testnet` — NEAR testnet account
- `staging.domain` — Staging domain
- `app.host`, `app.ui`, `app.api`, `app.auth` — Module configs with development/production URLs
- `plugins.{key}` — Plugin configs with variables, secrets, routes
- `app.api.shared`, `app.auth.shared`, `plugins.{key}.shared` — Module Federation shared dependency versions
### extends
Config can inherit from a parent via `extends`:
```json
{ "extends": "bos://dev.everything.near/everything.dev" }
```
Or per-environment:
```json
{
"extends": {
"development": "bos://dev.everything.near/everything.dev",
"production": "bos://dev.everything.near/everything.dev",
"staging": "bos://staging.everything.near/everything.dev"
}
}
```
Deep merge: child overrides parent. Plugins are deep-merged (set to `null` to remove). `secrets` arrays are unioned. See the `extends-config` skill for full details.
Registry and discovery should treat `extends` as runtime lineage. That keeps remix ancestry, tenant roots, and published BOS refs aligned without adding a second parent field.
For remix-host browsing with the apps plugin:
- use `parent` when you want only direct children of a runtime
- use `ancestor` when you want all descendants of a runtime, even when that runtime is not the lineage root
- use `root` when you want the whole tree from the topmost ancestor
- prefer querying by canonical BOS ref like `bos://account/gateway`, not by host URL, because shared-host descendants can reuse the same host
### What bos dev writes vs bos publish writes
See `everything-dev#extends-config` for the full table. In short: `bos dev` and `bos build` write to `.bos/bos.resolved-config.json` (gitignored); `bos publish --deploy`, `bos plugin publish`, and `bos sync` write to the authored config — `bos.app.ts` for TS-form projects, `bos.config.json` for legacy JSON-form children. Pipeline state (deploy-injected URLs) is written back only for JSON-form.
## Troubleshooting
```bash
bos info # Show current configuration
bos status # Check remote health
```
### FastKV publish failures
- **NEAR RPC error**: Verify the configured NEAR account has sufficient gas and the FastKV contract is deployed at `{account}/bos/gateways/{gateway}/bos.config.json`
- **Account mismatch**: The authored config's `account` field must match the on-chain account that owns the FastKV path
- **Network mismatch**: Ensure `--network` matches where the account is deployed (mainnet vs testnet)
- **Dry-run first**: Use `bos publish --dry-run` to preview before sending
### Integrity verification
During `bos publish --deploy`, integrity SRI hashes are auto-generated for each remote entry and stored in the published config (FastKV; also written back to `bos.config.json` for legacy JSON-form children). At runtime, the host:
- Verifies integrity on first load using bounded streaming (not full-response buffering)
- Uses stale-while-revalidate for asset requests to avoid latency spikes
- Blocks HTML and SSR requests on integrity mismatch
- Identifies SSR modules by both URL and `ssrIntegrity` hash in the cache
If a remote entry fails integrity check, the host rejects it and falls back to client-rendered output without that remote.
### Federation runtime compatibility (`bos mf check`)
Each deployed bundle's `mf-manifest.json` reports `metaData.pluginVersion` — the version of `@module-federation/runtime` baked into the bundle at build time. The host is the federation **consumer**; each plugin is a **provider** loaded over `mf-manifest.json` and `remoteEntry.js`. When the host's runtime version drifts ahead of a plugin's runtime version, the plugin's `remoteEntry.js` throws:
```
undefined is not an object (evaluating '__webpack_modules__[e].call')
```
The host treats this as a soft failure and continues serving with the plugin missing (`⚠ N plugin(s) failed to load`). Production stays live but the plugin is dead.
```bash
bos mf check # exit 0 = all manifests compatible
bos publish --deploy --packages local # ship a fresh bundle + republish to FastKV
```
The check fetches `<host>/mf-manifest.json` and every `<plugin>/mf-manifest.json`, asserting:
- `metaData.pluginVersion` matches the host's
- every `shared[]` dep the host requires (`requiredVersion: ^X.Y.Z`) is provided by the plugin at a compatible version
If `bos mf check` fails for one plugin, redeploy only that plugin from this repo:
```bash
cd plugins/<key>
bos plugin publish <key> # rebuild + pin the workspace's production URL, then publish config
```
Then from the repo root: `bos publish --deploy --packages local` and `bos mf check` to confirm.
In CI, this runs as a `bos mf check` step on every push/PR (`packages/everything-dev/src/cli.ts:mfCheck`). Treat a failed run as "this bundle is stale; a plugin needs to be redeployed" — never disable.
### Process issues
```bash
bos kill # Kill all tracked processes
pnpm install # Reinstall deps
bos dev # Restart
```
## The tasks you will actually be given
**"Ship one plugin only."** `bos plugin publish <key>` (builds + pins the deterministic URL + publishes config), then `bos mf check` from the repo root to confirm federation compatibility.
**"Roll back a bad publish."** `bos rollback --previous` — republishes a verified FastKV snapshot without touching git; the manual path is `git checkout <ref> -- bos.app.ts` + `bos publish`.
**"Deploy my own runtime under my own account."** NEAR account + `bos key generate` → `NEAR_PRIVATE_KEY`; authored config sets your `account` + `extends` the parent and keeps `domain` as the parent gateway; `bos publish --deploy`; run the image with `BOS_ACCOUNT` / `BOS_GATEWAY`.
## What comes back when it refuses
| word | do |
|---|---|
| publish returns `status: "error"` before signing | preflight failed — key resolution (no `NEAR_PRIVATE_KEY` / credentials / keychain) or the registry read; run `bos publish --dry-run` to see the plan |
| config resolves to nothing at `bos://<account>/<gateway>` | namespace = signer — the tx was signed by a different account than the config's `account`; sign with the publisher's key (see the `registry` skill) |
| `integrity mismatch for <url>` | the remote entry's SRI hash diverges from the published config — redeploy that workspace and republish; the host blocks HTML/SSR on mismatch and client-renders without the remote |
| `N plugin(s) failed to load` at host startup | `bos mf check` — a plugin's `metaData.pluginVersion` lags the host; republish that plugin |
| `missing-local-path` error on `--packages local` | a selected entry has no `development: local:` (remote-only) — it can't be rebuilt from this repo; drop it from the selection |
| publish prints nothing / skips | FastKV already holds an identical config — a free no-op, nothing to fix |