From 86edb1694781c0464b7dab10f1baa7063834689b Mon Sep 17 00:00:00 2001 From: Jarvis Date: Sun, 19 Apr 2026 18:06:37 -0500 Subject: [PATCH] =?UTF-8?q?feat(config):=20add=20federated=20tier=20and=20?= =?UTF-8?q?rename=20team=E2=86=92standalone=20(FED-M1-01)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Aligns mosaic.config tier vocabulary with the federation PRD: - Adds `federated` to the tier enum (Postgres + Valkey + pgvector) - Renames `team` → `standalone` in StorageTier / GatewayStorageTier - Keeps `team` as a deprecated alias that warns to stderr and maps to standalone - Adds DEFAULT_FEDERATED_CONFIG (port 5433, pgvector memory) - Corrects DEFAULT_STANDALONE_CONFIG.memory to keyword (PRD line 247) - Wizard interactive promptTier offers local/standalone/federated - Wizard headless env path now hard-rejects unknown MOSAIC_STORAGE_TIER values with a message naming all three valid tiers - Interactive DATABASE_URL pre-fill is tier-aware: standalone→5432, federated→5433 - 10 new unit tests for the validator + 3 new wizard headless tests No new behavior beyond the enum and defaults — pgvector enforcement, tier-detector, and docker-compose overlays land in FED-M1-02..04. Refs #460 Co-Authored-By: Claude Opus 4.7 --- docs/TASKS.md | 2 +- docs/federation/TASKS.md | 2 +- docs/scratchpads/mvp-20260312.md | 38 ++++++ packages/config/src/index.ts | 3 +- packages/config/src/mosaic-config.spec.ts | 109 ++++++++++++++++++ packages/config/src/mosaic-config.ts | 31 +++-- .../mosaic/src/stages/gateway-config.spec.ts | 71 +++++++++++- packages/mosaic/src/stages/gateway-config.ts | 60 +++++++--- packages/mosaic/src/types.ts | 2 +- 9 files changed, 288 insertions(+), 30 deletions(-) create mode 100644 packages/config/src/mosaic-config.spec.ts diff --git a/docs/TASKS.md b/docs/TASKS.md index a7d956d..49d9d03 100644 --- a/docs/TASKS.md +++ b/docs/TASKS.md @@ -28,7 +28,7 @@ These are MVP-level checks that don't belong to any single workstream. Updated b | MVP-T02 | done | Archive install-ux-v2 mission state to `docs/archive/missions/install-ux-v2-20260405/` | IUV-M03 retroactively closed (shipped via PR #446 + releases 0.0.27→0.0.29) | | MVP-T03 | done | Land federation v1 planning artifacts on `main` | PR #468 merged 2026-04-19 (commit `66512550`) | | MVP-T04 | not-started | Sync `.mosaic/orchestrator/mission.json` MVP slot with this manifest (milestone enumeration, etc.) | Coord state file; consider whether to repopulate via `mosaic coord` or accept hand-edit | -| MVP-T05 | not-started | Kick off W1 / FED-M1 — federated tier infrastructure | First execution task in MVP | +| MVP-T05 | in-progress | Kick off W1 / FED-M1 — federated tier infrastructure | Session 16 (2026-04-19): FED-M1-01 in-progress on `feat/federation-m1-tier-config` | | MVP-T06 | not-started | Declare additional workstreams (web dashboard, TUI/CLI parity, remote control, etc.) as scope solidifies | Track each new workstream by adding a row to the Workstream Rollup | ## Pointer to Active Workstream diff --git a/docs/federation/TASKS.md b/docs/federation/TASKS.md index d004b66..1692284 100644 --- a/docs/federation/TASKS.md +++ b/docs/federation/TASKS.md @@ -17,7 +17,7 @@ Goal: Gateway runs in `federated` tier with containerized PG+pgvector+Valkey. No | id | status | description | issue | agent | branch | depends_on | estimate | notes | | --------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----- | ------ | ------------------------------- | ---------- | -------- | ----------------------------------------------------------------------------------------------------------------- | -| FED-M1-01 | not-started | Extend `mosaic.config.json` schema: add `"federated"` to `tier` enum in validator + TS types. Keep `local` and `standalone` working. Update schema docs/README where referenced. | #460 | codex | feat/federation-m1-tier-config | — | 4K | Schema lives in `packages/types`; validator in gateway bootstrap. No behavior change yet — enum only. | +| FED-M1-01 | in-progress | Extend `mosaic.config.json` schema: add `"federated"` to `tier` enum in validator + TS types. Keep `local` and `standalone` working. Update schema docs/README where referenced. | #460 | sonnet | feat/federation-m1-tier-config | — | 4K | Schema lives in `packages/types`; validator in gateway bootstrap. No behavior change yet — enum only. | | FED-M1-02 | not-started | Author `docker-compose.federated.yml` as an overlay profile: Postgres 16 + pgvector extension (port 5433), Valkey (6380), named volumes, healthchecks. Compose-up should boot cleanly on a clean machine. | #460 | codex | feat/federation-m1-compose | FED-M1-01 | 5K | Overlay on existing `docker-compose.yml`; no changes to base file. Add `profile: federated` gating. | | FED-M1-03 | not-started | Add pgvector support to `packages/storage/src/adapters/postgres.ts`: create extension on init (idempotent), expose vector column type in schema helpers. No adapter changes for non-federated tiers. | #460 | codex | feat/federation-m1-pgvector | FED-M1-02 | 8K | Extension create is idempotent `CREATE EXTENSION IF NOT EXISTS vector`. Gate on tier = federated. | | FED-M1-04 | not-started | Implement `apps/gateway/src/bootstrap/tier-detector.ts`: reads config, asserts PG/Valkey/pgvector reachable for `federated`, fail-fast with actionable error message on failure. Unit tests for each failure mode. | #460 | codex | feat/federation-m1-detector | FED-M1-03 | 8K | Structured error type with remediation hints. Logs which service failed, with host:port attempted. | diff --git a/docs/scratchpads/mvp-20260312.md b/docs/scratchpads/mvp-20260312.md index 51c75e7..2fbd5e2 100644 --- a/docs/scratchpads/mvp-20260312.md +++ b/docs/scratchpads/mvp-20260312.md @@ -305,3 +305,41 @@ Issues closed: #52, #55, #57, #58, #120-#134 | Install-ux-v2 manifest + tasks + scratchpad + iuv-m03-design | moved to `docs/archive/missions/install-ux-v2-20260405/` with status corrected to complete | **Next:** PR `docs/mvp-mission-manifest` → merge to `main` → next session begins W1 / FED-M1 from clean state. + +--- + +## Session 16 — 2026-04-19 — claude + +**Mode:** Delivery (W1 / FED-M1 execution) +**Branch:** `feat/federation-m1-tier-config` +**Context budget:** 200K, currently ~45% used (compaction-aware) + +**Goal:** FED-M1-01 — extend `mosaic.config.json` schema: add `"federated"` to tier enum. + +**Critical reconciliation surfaced during pre-flight:** + +The federation PRD (`docs/federation/PRD.md` line 247) defines three tiers: `local | standalone | federated`. +The existing code (`packages/config/src/mosaic-config.ts`, `packages/mosaic/src/types.ts`, `packages/mosaic/src/stages/gateway-config.ts`) uses `local | team`. + +`team` is the same conceptual tier as PRD `standalone` (Postgres + Valkey, no pgvector). Rather than carrying a confusing alias forever, FED-M1-01 will rename `team` → `standalone` and add `federated` as a third value, so all downstream federation work has a coherent vocabulary. + +Affected files (storage-tier semantics only — Team/workspace usages unaffected): + +- `packages/config/src/mosaic-config.ts` (StorageTier type, validator enum, defaults) +- `packages/mosaic/src/types.ts` (GatewayStorageTier) +- `packages/mosaic/src/stages/gateway-config.ts` (~10 references) +- `packages/mosaic/src/stages/gateway-config.spec.ts` (test references) +- Possibly `tools/e2e-install-test.sh` (referenced grep) and headless env hint string + +**Worker plan:** + +1. Spawn sonnet subagent with explicit task spec + the reconciliation context above. +2. Worker delivers diff; orchestrator runs `pnpm typecheck && pnpm lint && pnpm format:check`. +3. Independent `feature-dev:code-reviewer` subagent reviews diff. +4. Second independent verification subagent (general-purpose, sonnet) verifies reviewer's claims and confirms all `'team'` storage-tier references migrated, no `Team`/workspace bleed. +5. Open PR via tea CLI; wait for CI; queue-guard; squash merge; record actuals. + +**Open items:** + +- `MVP-T04` (sync `.mosaic/orchestrator/mission.json`) still deferred. +- `team` tier rename touches install wizard headless env vars (`MOSAIC_STORAGE_TIER=team`); will need 0.0.x deprecation note in scratchpad if release notes are written this milestone. diff --git a/packages/config/src/index.ts b/packages/config/src/index.ts index 79e2526..b5769b1 100644 --- a/packages/config/src/index.ts +++ b/packages/config/src/index.ts @@ -1,7 +1,8 @@ export type { MosaicConfig, StorageTier, MemoryConfigRef } from './mosaic-config.js'; export { DEFAULT_LOCAL_CONFIG, - DEFAULT_TEAM_CONFIG, + DEFAULT_STANDALONE_CONFIG, + DEFAULT_FEDERATED_CONFIG, loadConfig, validateConfig, } from './mosaic-config.js'; diff --git a/packages/config/src/mosaic-config.spec.ts b/packages/config/src/mosaic-config.spec.ts new file mode 100644 index 0000000..6d52c97 --- /dev/null +++ b/packages/config/src/mosaic-config.spec.ts @@ -0,0 +1,109 @@ +import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; +import { + validateConfig, + DEFAULT_LOCAL_CONFIG, + DEFAULT_STANDALONE_CONFIG, + DEFAULT_FEDERATED_CONFIG, +} from './mosaic-config.js'; + +describe('validateConfig — tier enum', () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let stderrSpy: any; + + beforeEach(() => { + stderrSpy = vi.spyOn(process.stderr, 'write').mockImplementation(() => true); + }); + + afterEach(() => { + stderrSpy.mockRestore(); + }); + + it('accepts tier="local"', () => { + const result = validateConfig({ + tier: 'local', + storage: { type: 'pglite', dataDir: '.mosaic/storage-pglite' }, + queue: { type: 'local', dataDir: '.mosaic/queue' }, + memory: { type: 'keyword' }, + }); + expect(result.tier).toBe('local'); + }); + + it('accepts tier="standalone"', () => { + const result = validateConfig({ + tier: 'standalone', + storage: { type: 'postgres', url: 'postgresql://mosaic:mosaic@localhost:5432/mosaic' }, + queue: { type: 'bullmq' }, + memory: { type: 'keyword' }, + }); + expect(result.tier).toBe('standalone'); + }); + + it('accepts tier="federated"', () => { + const result = validateConfig({ + tier: 'federated', + storage: { type: 'postgres', url: 'postgresql://mosaic:mosaic@localhost:5433/mosaic' }, + queue: { type: 'bullmq' }, + memory: { type: 'pgvector' }, + }); + expect(result.tier).toBe('federated'); + }); + + it('accepts deprecated tier="team" as alias for "standalone" and emits a deprecation warning', () => { + const result = validateConfig({ + tier: 'team', + storage: { type: 'postgres', url: 'postgresql://mosaic:mosaic@localhost:5432/mosaic' }, + queue: { type: 'bullmq' }, + memory: { type: 'keyword' }, + }); + expect(result.tier).toBe('standalone'); + expect(stderrSpy).toHaveBeenCalledWith(expect.stringContaining('DEPRECATED')); + }); + + it('rejects an invalid tier with an error listing all three valid values', () => { + expect(() => + validateConfig({ + tier: 'invalid', + storage: { type: 'postgres', url: 'postgresql://mosaic:mosaic@localhost:5432/mosaic' }, + queue: { type: 'bullmq' }, + memory: { type: 'keyword' }, + }), + ).toThrow(/local.*standalone.*federated|federated.*standalone.*local/); + }); + + it('error message for invalid tier mentions all three valid values', () => { + let message = ''; + try { + validateConfig({ + tier: 'invalid', + storage: { type: 'postgres', url: 'postgresql://...' }, + queue: { type: 'bullmq' }, + memory: { type: 'keyword' }, + }); + } catch (err) { + message = err instanceof Error ? err.message : String(err); + } + expect(message).toContain('"local"'); + expect(message).toContain('"standalone"'); + expect(message).toContain('"federated"'); + }); +}); + +describe('DEFAULT_* config constants', () => { + it('DEFAULT_LOCAL_CONFIG has tier="local"', () => { + expect(DEFAULT_LOCAL_CONFIG.tier).toBe('local'); + }); + + it('DEFAULT_STANDALONE_CONFIG has tier="standalone"', () => { + expect(DEFAULT_STANDALONE_CONFIG.tier).toBe('standalone'); + }); + + it('DEFAULT_FEDERATED_CONFIG has tier="federated" and pgvector memory', () => { + expect(DEFAULT_FEDERATED_CONFIG.tier).toBe('federated'); + expect(DEFAULT_FEDERATED_CONFIG.memory.type).toBe('pgvector'); + }); + + it('DEFAULT_FEDERATED_CONFIG uses port 5433 (distinct from standalone 5432)', () => { + const url = (DEFAULT_FEDERATED_CONFIG.storage as { url: string }).url; + expect(url).toContain('5433'); + }); +}); diff --git a/packages/config/src/mosaic-config.ts b/packages/config/src/mosaic-config.ts index 29aa078..d9c72ff 100644 --- a/packages/config/src/mosaic-config.ts +++ b/packages/config/src/mosaic-config.ts @@ -7,7 +7,7 @@ import type { QueueAdapterConfig as QueueConfig } from '@mosaicstack/queue'; /* Types */ /* ------------------------------------------------------------------ */ -export type StorageTier = 'local' | 'team'; +export type StorageTier = 'local' | 'standalone' | 'federated'; export interface MemoryConfigRef { type: 'pgvector' | 'sqlite-vec' | 'keyword'; @@ -31,10 +31,17 @@ export const DEFAULT_LOCAL_CONFIG: MosaicConfig = { memory: { type: 'keyword' }, }; -export const DEFAULT_TEAM_CONFIG: MosaicConfig = { - tier: 'team', +export const DEFAULT_STANDALONE_CONFIG: MosaicConfig = { + tier: 'standalone', storage: { type: 'postgres', url: 'postgresql://mosaic:mosaic@localhost:5432/mosaic' }, queue: { type: 'bullmq' }, + memory: { type: 'keyword' }, +}; + +export const DEFAULT_FEDERATED_CONFIG: MosaicConfig = { + tier: 'federated', + storage: { type: 'postgres', url: 'postgresql://mosaic:mosaic@localhost:5433/mosaic' }, + queue: { type: 'bullmq' }, memory: { type: 'pgvector' }, }; @@ -42,7 +49,7 @@ export const DEFAULT_TEAM_CONFIG: MosaicConfig = { /* Validation */ /* ------------------------------------------------------------------ */ -const VALID_TIERS = new Set(['local', 'team']); +const VALID_TIERS = new Set(['local', 'standalone', 'federated']); const VALID_STORAGE_TYPES = new Set(['postgres', 'pglite', 'files']); const VALID_QUEUE_TYPES = new Set(['bullmq', 'local']); const VALID_MEMORY_TYPES = new Set(['pgvector', 'sqlite-vec', 'keyword']); @@ -55,9 +62,19 @@ export function validateConfig(raw: unknown): MosaicConfig { const obj = raw as Record; // tier - const tier = obj['tier']; + let tier = obj['tier']; + // Deprecated alias: 'team' → 'standalone' (kept for backward-compat with 0.0.x installs) + if (tier === 'team') { + process.stderr.write( + '[mosaic] DEPRECATED: tier="team" is deprecated — use "standalone" instead. ' + + 'Update your mosaic.config.json.\n', + ); + tier = 'standalone'; + } if (typeof tier !== 'string' || !VALID_TIERS.has(tier)) { - throw new Error(`Invalid tier "${String(tier)}" — expected "local" or "team"`); + throw new Error( + `Invalid tier "${String(tier)}" — expected "local", "standalone", or "federated"`, + ); } // storage @@ -105,7 +122,7 @@ export function validateConfig(raw: unknown): MosaicConfig { function detectFromEnv(): MosaicConfig { if (process.env['DATABASE_URL']) { return { - ...DEFAULT_TEAM_CONFIG, + ...DEFAULT_STANDALONE_CONFIG, storage: { type: 'postgres', url: process.env['DATABASE_URL'], diff --git a/packages/mosaic/src/stages/gateway-config.spec.ts b/packages/mosaic/src/stages/gateway-config.spec.ts index b4173b3..fe71e48 100644 --- a/packages/mosaic/src/stages/gateway-config.spec.ts +++ b/packages/mosaic/src/stages/gateway-config.spec.ts @@ -216,8 +216,8 @@ describe('gatewayConfigStage', () => { expect(daemonState.startCalled).toBe(0); }); - it('honors MOSAIC_STORAGE_TIER=team in headless path', async () => { - process.env['MOSAIC_STORAGE_TIER'] = 'team'; + it('honors MOSAIC_STORAGE_TIER=standalone in headless path', async () => { + process.env['MOSAIC_STORAGE_TIER'] = 'standalone'; process.env['MOSAIC_DATABASE_URL'] = 'postgresql://test/db'; process.env['MOSAIC_VALKEY_URL'] = 'redis://test:6379'; @@ -231,12 +231,75 @@ describe('gatewayConfigStage', () => { }); expect(result.ready).toBe(true); - expect(state.gateway?.tier).toBe('team'); + expect(state.gateway?.tier).toBe('standalone'); const envContents = readFileSync(daemonState.envFile, 'utf-8'); expect(envContents).toContain('DATABASE_URL=postgresql://test/db'); expect(envContents).toContain('VALKEY_URL=redis://test:6379'); const mosaicConfig = JSON.parse(readFileSync(daemonState.mosaicConfigFile, 'utf-8')); - expect(mosaicConfig.tier).toBe('team'); + expect(mosaicConfig.tier).toBe('standalone'); + }); + + it('accepts deprecated MOSAIC_STORAGE_TIER=team as alias for standalone', async () => { + process.env['MOSAIC_STORAGE_TIER'] = 'team'; + process.env['MOSAIC_DATABASE_URL'] = 'postgresql://test/db'; + process.env['MOSAIC_VALKEY_URL'] = 'redis://test:6379'; + + const p = buildPrompter(); + const state = makeState('/home/user/.config/mosaic'); + + const result = await gatewayConfigStage(p, state, { + host: 'localhost', + defaultPort: 14242, + skipInstall: true, + }); + + // Deprecated alias 'team' maps to 'standalone' + expect(result.ready).toBe(true); + expect(state.gateway?.tier).toBe('standalone'); + const mosaicConfig = JSON.parse(readFileSync(daemonState.mosaicConfigFile, 'utf-8')); + expect(mosaicConfig.tier).toBe('standalone'); + }); + + it('honors MOSAIC_STORAGE_TIER=federated in headless path', async () => { + process.env['MOSAIC_STORAGE_TIER'] = 'federated'; + process.env['MOSAIC_DATABASE_URL'] = 'postgresql://test/feddb'; + process.env['MOSAIC_VALKEY_URL'] = 'redis://test:6379'; + + const p = buildPrompter(); + const state = makeState('/home/user/.config/mosaic'); + + const result = await gatewayConfigStage(p, state, { + host: 'localhost', + defaultPort: 14242, + skipInstall: true, + }); + + expect(result.ready).toBe(true); + expect(state.gateway?.tier).toBe('federated'); + const envContents = readFileSync(daemonState.envFile, 'utf-8'); + expect(envContents).toContain('DATABASE_URL=postgresql://test/feddb'); + const mosaicConfig = JSON.parse(readFileSync(daemonState.mosaicConfigFile, 'utf-8')); + expect(mosaicConfig.tier).toBe('federated'); + expect(mosaicConfig.memory.type).toBe('pgvector'); + }); + + it('rejects an unknown MOSAIC_STORAGE_TIER value in headless mode with a descriptive warning', async () => { + process.env['MOSAIC_STORAGE_TIER'] = 'federatd'; // deliberate typo + + const warnFn = vi.fn(); + const p = buildPrompter({ warn: warnFn }); + const state = makeState('/home/user/.config/mosaic'); + + const result = await gatewayConfigStage(p, state, { + host: 'localhost', + defaultPort: 14242, + skipInstall: true, + }); + + // The stage surfaces validation errors as ready:false (warning is shown to the user). + expect(result.ready).toBe(false); + // The warning message must name all three valid values. + expect(warnFn).toHaveBeenCalledWith(expect.stringMatching(/local.*standalone.*federated/i)); }); it('regenerates config when portOverride differs from saved GATEWAY_PORT', async () => { diff --git a/packages/mosaic/src/stages/gateway-config.ts b/packages/mosaic/src/stages/gateway-config.ts index 5265747..fdfd8ba 100644 --- a/packages/mosaic/src/stages/gateway-config.ts +++ b/packages/mosaic/src/stages/gateway-config.ts @@ -84,10 +84,15 @@ async function promptTier(p: WizardPrompter): Promise { hint: 'embedded database, no dependencies', }, { - value: 'team', - label: 'Team', + value: 'standalone', + label: 'Standalone', hint: 'PostgreSQL + Valkey required', }, + { + value: 'federated', + label: 'Federated', + hint: 'PostgreSQL + Valkey + pgvector, federation server+client', + }, ], }); return tier; @@ -437,7 +442,21 @@ async function collectAndWriteConfig( p.log('Headless mode detected — reading configuration from environment variables.'); const storageTierEnv = process.env['MOSAIC_STORAGE_TIER'] ?? 'local'; - tier = storageTierEnv === 'team' ? 'team' : 'local'; + if (storageTierEnv === 'team') { + // Deprecated alias — warn and treat as standalone + process.stderr.write( + '[mosaic] DEPRECATED: MOSAIC_STORAGE_TIER=team is deprecated — use "standalone" instead.\n', + ); + tier = 'standalone'; + } else if (storageTierEnv === 'standalone' || storageTierEnv === 'federated') { + tier = storageTierEnv; + } else if (storageTierEnv !== '' && storageTierEnv !== 'local') { + throw new GatewayConfigValidationError( + `Invalid MOSAIC_STORAGE_TIER="${storageTierEnv}" — expected "local", "standalone", or "federated" (deprecated alias "team" also accepted)`, + ); + } else { + tier = 'local'; + } const portEnv = process.env['MOSAIC_GATEWAY_PORT']; port = portEnv ? parseInt(portEnv, 10) : opts.defaultPort; @@ -453,13 +472,13 @@ async function collectAndWriteConfig( hostname = hostnameEnv; corsOrigin = corsOverride ?? deriveCorsOrigin(hostnameEnv, 3000); - if (tier === 'team') { + if (tier === 'standalone' || tier === 'federated') { const missing: string[] = []; if (!databaseUrl) missing.push('MOSAIC_DATABASE_URL'); if (!valkeyUrl) missing.push('MOSAIC_VALKEY_URL'); if (missing.length > 0) { throw new GatewayConfigValidationError( - 'Headless install with tier=team requires env vars: ' + missing.join(', '), + `Headless install with tier=${tier} requires env vars: ` + missing.join(', '), ); } } @@ -467,11 +486,15 @@ async function collectAndWriteConfig( tier = await promptTier(p); port = await promptPort(p, opts.defaultPort); - if (tier === 'team') { + if (tier === 'standalone' || tier === 'federated') { + const defaultDbUrl = + tier === 'federated' + ? 'postgresql://mosaic:mosaic@localhost:5433/mosaic' + : 'postgresql://mosaic:mosaic@localhost:5432/mosaic'; databaseUrl = await p.text({ message: 'DATABASE_URL', - initialValue: 'postgresql://mosaic:mosaic@localhost:5433/mosaic', - defaultValue: 'postgresql://mosaic:mosaic@localhost:5433/mosaic', + initialValue: defaultDbUrl, + defaultValue: defaultDbUrl, }); valkeyUrl = await p.text({ message: 'VALKEY_URL', @@ -521,7 +544,7 @@ async function collectAndWriteConfig( `OTEL_SERVICE_NAME=mosaic-gateway`, ]; - if (tier === 'team' && databaseUrl && valkeyUrl) { + if ((tier === 'standalone' || tier === 'federated') && databaseUrl && valkeyUrl) { envLines.push(`DATABASE_URL=${databaseUrl}`); envLines.push(`VALKEY_URL=${valkeyUrl}`); } @@ -545,12 +568,19 @@ async function collectAndWriteConfig( queue: { type: 'local', dataDir: join(opts.gatewayHome, 'queue') }, memory: { type: 'keyword' }, } - : { - tier: 'team', - storage: { type: 'postgres', url: databaseUrl }, - queue: { type: 'bullmq', url: valkeyUrl }, - memory: { type: 'pgvector' }, - }; + : tier === 'federated' + ? { + tier: 'federated', + storage: { type: 'postgres', url: databaseUrl }, + queue: { type: 'bullmq', url: valkeyUrl }, + memory: { type: 'pgvector' }, + } + : { + tier: 'standalone', + storage: { type: 'postgres', url: databaseUrl }, + queue: { type: 'bullmq', url: valkeyUrl }, + memory: { type: 'keyword' }, + }; writeFileSync(opts.mosaicConfigFile, JSON.stringify(mosaicConfig, null, 2) + '\n', { mode: 0o600, diff --git a/packages/mosaic/src/types.ts b/packages/mosaic/src/types.ts index 1323258..7d04cf2 100644 --- a/packages/mosaic/src/types.ts +++ b/packages/mosaic/src/types.ts @@ -58,7 +58,7 @@ export interface HooksState { acceptedAt?: string; } -export type GatewayStorageTier = 'local' | 'team'; +export type GatewayStorageTier = 'local' | 'standalone' | 'federated'; export interface GatewayAdminState { name: string;