feat(FN-4913): complete Step 8 — update docs and changeset

Fusion-Task-Id: FN-4913
Fusion-Task-Lineage: 2fa1fe49-a2a9-405d-833b-cb34d41ef19a
This commit is contained in:
Fusion (runfusion.ai)
2026-05-17 12:30:11 -07:00
committed by gsxdsm
parent cca96a7604
commit e9ef40f6a7
3 changed files with 31 additions and 9 deletions

View File

@@ -0,0 +1,5 @@
---
"@runfusion/fusion": minor
---
Add cross-node secrets sync endpoints (`POST /api/nodes/:id/secrets/push`, `POST /api/nodes/:id/secrets/pull`, `POST /api/secrets/sync-receive`, `GET /api/secrets/sync-export`) with shared-passphrase envelope (scrypt → AES-256-GCM) and Bearer-apiKey auth on inbound routes. Passphrase is stored locally encrypted under the master key (reserved `__sync_passphrase__` row, `access_policy="deny"`) and is never transmitted or echoed.

View File

@@ -996,17 +996,18 @@ The client treats mapping persistence as part of onboarding success. If mapping
| POST | `/api/nodes/:id/settings/pull` | Pull settings from a remote node. |
| GET | `/api/nodes/:id/settings/sync-status` | Get sync status and diff summary (includes `actionableDenialReason` when remote probe fails). |
| POST | `/api/nodes/:id/auth/sync` | Sync model auth snapshots (push/pull, checksum/version validated). |
| POST | `/api/nodes/:id/secrets/push` | Push local secrets snapshot to a remote node *(planned; follow-up FN-4867)*. |
| POST | `/api/nodes/:id/secrets/pull` | Pull secrets snapshot from a remote node *(planned; follow-up FN-4867)*. |
| POST | `/api/nodes/:id/secrets/push` | Push local secrets snapshot to a remote node. |
| POST | `/api/nodes/:id/secrets/pull` | Pull secrets snapshot from a remote node. |
| POST | `/api/settings/sync-receive` | Receive pushed settings (inbound). |
| POST | `/api/settings/auth-receive` | Receive `AuthMaterialSnapshot` and persist via auth storage. |
| POST | `/api/secrets/sync-receive` | Receive pushed secrets payload (inbound, planned; follow-up FN-4867). |
| POST | `/api/secrets/sync-receive` | Receive pushed secrets payload (inbound). |
| GET | `/api/secrets/sync-export` | Export local secrets sync envelope for remote pull flows. |
| GET | `/api/settings/auth-export` | Export local `AuthMaterialSnapshot`. |
| GET | `/api/update-check` | Read cached/TTL-guarded npm update status for `@runfusion/fusion` (respects `updateCheckEnabled`). |
| POST | `/api/update-check/refresh` | Clear cached update data and force a fresh npm update check. |
| GET | `/api/updates/check` | Perform an on-demand npm registry check for the latest `@runfusion/fusion` version (no cache). |
When adding a new node settings/auth sync endpoint, add it to the `ENDPOINTS` catalog in `packages/dashboard/src/__tests__/routes-nodes-sync-contract.test.ts` so the auth/error/payload parity matrix covers it. Inbound sync endpoints (including planned `/api/secrets/sync-receive`) must validate `Authorization: Bearer <apiKey>` against the local node API key.
When adding a new node settings/auth sync endpoint, add it to the `ENDPOINTS` catalog in `packages/dashboard/src/__tests__/routes-nodes-sync-contract.test.ts` so the auth/error/payload parity matrix covers it. Inbound sync endpoints (including `/api/secrets/sync-receive` and `/api/secrets/sync-export`) must validate `Authorization: Bearer <apiKey>` against the local node API key.
### Agent stats endpoint

View File

@@ -103,13 +103,29 @@ The schema already carries env-materialization metadata (`env_exportable`, `env_
## Cross-node Sync
⚠️ Secrets sync endpoints are not yet present in this branch:
Fusion now exposes four secrets sync endpoints:
- `POST /api/nodes/:id/secrets/push`
- `POST /api/nodes/:id/secrets/pull`
- `POST /api/secrets/sync-receive`
- `POST /api/nodes/:id/secrets/push` — wraps local secrets into a passphrase-protected envelope and sends it to a remote node.
- `POST /api/nodes/:id/secrets/pull` — fetches a remote envelope from `GET /api/secrets/sync-export` and applies it locally.
- `POST /api/secrets/sync-receive` — inbound apply endpoint (Bearer `apiKey` required).
- `GET /api/secrets/sync-export` — inbound export endpoint (Bearer `apiKey` required).
Passphrase-based sync wrapping/KDF behavior is also pending; see follow-up **FN-4867**.
Envelope format is `WrappedSecretsBundle` from `packages/core/src/secrets-sync.ts`: `{ version, ciphertext, salt, nonce, kdf, kdfParams }` plus transport metadata (`sourceNodeId`, `exportedAt`). Wrapping uses scrypt (`N=32768, r=8, p=1, keyLen=32`) and AES-256-GCM with fresh 12-byte nonce + 16-byte salt per export. `TODO(FN-4867)` remains for planned Argon2id migration.
Sync passphrase storage is local-only: reserved key `__sync_passphrase__` in `secrets_global` with `access_policy="deny"` and `env_exportable=false`, encrypted under the local master key. The passphrase is never transmitted and never returned by HTTP endpoints.
Error mapping:
- `SecretsSyncError` codes (`wrong-passphrase`, `version-mismatch`, `malformed`) return HTTP `400` with `{ "error": <code> }`.
- Missing passphrase returns HTTP `400` with `{ "error": "passphrase-not-configured" }`.
- Bearer auth failures return HTTP `401`.
Audit events emitted on apply/send paths:
- `secret:sync-push`
- `secret:sync-pull`
Audit payloads exclude plaintext values, passphrases, and envelope crypto material (`ciphertext`, `salt`, `nonce`).
## Audit Events