---
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 |
