Status:
draft · Version 0.1 · Filed 2026-04-29version: 0.1 status: draft
Title: SPEC-055 v0.1 — Generic Operator Lifecycle: Target-Registry-Driven LAN install/upgrade/clone-db/backup, No Hardcoded Host Identifiers
Version
0.1Status
draftOrigin
The current operator lifecycle (prism_deploy, prism_clone_from, prism_backup MCP verbs + their shell wrappers bin/prism-deploy-server1.sh, scripts/clone-server1-to-local.sh) is hardcoded to one specific deployment: donna@server1.home.lan, /home/frank/Prism, prism-server-postgres, neo4j password prism_server, etc. SPEC-051 Phase 1 v0.1 explicitly punted on multi-target (“only target='server1' supported”). The Python server1_config() function is a literal: anyone else running Prism on their own server has to set ~10 env vars or fork the source.
Frank’s correction during the SPEC-054 tier 1C-redux design discussion: this is a commercial product. The operator-side lifecycle must be commercial-grade — generic enough that any user, on any server, can use it without forking. Names describe the operation, not the host. Per-deployment data lives in config/env/db, not source.
This spec defines the multi-target replacement and retires the server1-shaped verbs.
§1 — Goals
- Zero machine-specific identifiers in source. No literal
server1,donna@,/home/frank,prism-server-postgresas defaults in any TS or Python file undercli/,mcp-node/, or anywhere a user pulling the repo would inherit them. - Operation-based naming. Verbs and CLI subcommands describe what they do (
install-lan,upgrade-lan,clone-db,backup), not which deployment they target. - Config-driven targets. A per-operator target registry (
~/.prism/targets.json) lists all named LAN/cloud destinations the user has configured. Verbs and CLI subcommands take a--target <name>arg that names a key in the registry. Empty registry → operator runsprism configure-lan(or edits the file) before any install/upgrade verb works. - Intent-first verbs.
prism_install_local,prism_install_lan,prism_upgrade_lan,prism_clone_db,prism_backup— separate verbs per intent, not one multiplexedprism_deploy(target=...). Aligns with feedback memoryfeedback_intent_is_first_class.md. - CLI is the primary surface. The operator interface is
prism install-lan,prism upgrade-lan, etc. — the same CLI that ownsprism install --mode localtoday. MCP verb wrappers exist for AI-FIRST coverage but delegate to the CLI implementation; both share the target-registry lookup. - Frank’s existing setup keeps working. Migration: a one-time
prism configure-lan --import-legacypulls his currentdonna@server1.home.lanconfig out of env-var defaults and writes it into~/.prism/targets.jsonas a named target (default namehome-lanor operator-chosen).
Non-goals (v0.1)
- Cloud / SaaS targets. Mode
cloudstays deferred per ADR-021 — same v0.1 cutoff. - Backend changes. Same as SPEC-054: backend Docker stack untouched.
- Multi-tenant target visibility. v0.1: targets are per-operator, in the operator’s home dir. Tenant-shared target registry is future work.
- Auto-discovery. Operator declares targets explicitly via
prism configure-lan; nothing auto-detects LAN servers.
§2 — Target Registry
Location
~/.prism/targets.json (overridable via PRISM_TARGETS_FILE env). Same dir as credentials.personal.json.
Schema
home → prism-home-postgres). Operator can override per-target.
CLI — prism configure-lan
Interactive add/edit. Prompts for ssh target, key, repo dir, backend URL, container-name pattern. Validates ssh connectivity (try a ssh user@host echo ok with BatchMode=yes) before persisting. Writes the new target into targets.json; offers to set as default_target if it’s the first.
Non-interactive form: prism configure-lan --name=<n> --ssh-target=<...> --ssh-key=<...> --repo-dir=<...> --backend-url=<...> for scripted setup.
CLI — prism configure-lan --import-legacy
One-shot migration. Reads PRISM_DEPLOY_* env vars + credentials.personal.json and writes them as a named target in targets.json. Default name home-lan; --name overrides. Idempotent. After this lands and Frank runs it once, his existing setup works unchanged through the new verb names.
§3 — Verb Surface
Retired verbs
prism_deploy(target='server1', mode='full')→ retired. Replaced byprism_upgrade_lan(target=<name>, mode='full').prism_clone_from(target='server1')→ retired. Replaced byprism_clone_db(from_target=<name>).
New verbs
| Verb | Args | Purpose |
|---|---|---|
prism_install_local | (none) | Bring up local Docker stack via docker-compose.personal.yml. Equivalent to prism install --mode local today. |
prism_install_lan | target: str | First install on a LAN target. Resolves target via registry, ssh+rsync repo, mints API key, writes credentials.personal.json (mode=lan), brings up docker-compose.server.yml on remote. Idempotent — re-running detects existing install and short-circuits. |
prism_upgrade_lan | target: str, mode: "full" | "build-only" | "verify-only" = "full" | Update existing LAN install. Today’s prism_deploy semantics, target-driven. |
prism_configure_lan | name: str, ssh_target: str, ssh_key: str, repo_dir: str, backend_url: str, containers?: dict | Add/edit a target in the registry. Validates connectivity. |
prism_clone_db | from_target: str | Mirror a remote target’s Postgres into the local stack (today’s prism_clone_from semantics, target-driven). |
prism_backup | dry_run: bool = false | Backup local user state. Unchanged from today. |
prism_list_targets | (none) | Return the registry contents (paths only — no secrets). |
Bootstrap-skip
All seven verbs are bootstrap-skipped (operator-side, not project-session-scoped). Same posture asprism_persona_create and prism_set_force_credentials today.
§4 — CLI Surface
prism install --mode local|lan form stays for one release as an alias. After that, the dashed forms (install-local, install-lan) are canonical.
prism install-lan requires the target to already exist in the registry. Error message points at prism configure-lan.
§5 — Implementation Layout
prism install --mode local already works (CLI implements; no MCP verb today).
§6 — Cutover Plan
Phase 1 — Land SPEC-055 implementation in cli/ + mcp-node/ (this spec)
- New
cli/src/targets.ts+cli/src/ops/*modules. - New CLI subcommands (
install-lan,upgrade-lan,configure-lan,clone-db,backup,list-targets). - New MCP verbs in
mcp-node/src/verbs/tier1c.tsreplacing the shell-out stubs. prism_deploy+prism_clone_fromregistered as retired (return retirement payload).- Legacy
mcp/deploy.py+mcp/backup.py+mcp/clone_from.py+bin/prism-deploy-server1.sh+bin/prism-backup.sh+scripts/clone-server1-to-local.shdeleted.
Phase 2 — Frank’s migration
- Frank runs
prism configure-lan --import-legacy --name home-lanonce. - Verifies
prism upgrade-lan --target home-lanworks.
Phase 3 — Retire the retirement (next release)
- Delete
mcp-node/src/verbs/retired.ts. - Drop
prism_deploy+prism_clone_fromfrommcp-node/src/verbs.tsregistry list.
§7 — Verification
git grep -nE 'server1|donna@|/home/frank' -- cli/ mcp-node/returns zero source references after Phase 1 (matches indocs/,archive/, changelog/retros are fine — those are historical record).~/.prism/targets.jsonafterprism configure-lan --import-legacyhas Frank’s existing setup as a named target.prism upgrade-lan --target home-lan --mode fullis functionally identical to today’sprism_deploy(target='server1', mode='full')— same rsync/build/restart/health/verify stages, same structured return.prism upgrade-lan --target <other-target>(e.g., a hypotheticalstagingtarget) works without code changes — only registry entry needed.- The MCP verb
prism_deployreturns the retirement payload withuse_instead: prism_upgrade_lan. - Empty registry:
prism upgrade-lanerrors with a clear message namingprism configure-lanas the fix.
§8 — Decisions
- D1 — CLI is primary, MCP verb is wrapper. Same posture as
prism install --mode localtoday (CLI-only, no MCP verb today). MCP wrappers exist for AI-FIRST. - D2 — Intent-first verb naming. Per
feedback_intent_is_first_class.md. Separateprism_install_lan/prism_upgrade_lan/prism_clone_dbrather than multiplexedprism_deploy(action=...). - D3 — Per-operator registry, not tenant-shared. v0.1 keeps targets in
~/.prism/. Tenant-shared registry is future work — needs auth + permission model that doesn’t exist yet. - D4 — Container names derive from target key by default. Override per-target. Avoids the operator having to specify three identical-shape container names every time.
- D5 — Retirement payload, not silent rename.
prism_deployreturns a structuredverb_retirederror withuse_insteadfor one release. Hard removal in next release. Gives any external scripts a deterministic upgrade path. - D6 —
--import-legacymigration. Frank gets a one-shot to convert his existing setup. Avoids “dies on first call after merge.” - D7 — Target registry schema is versioned.
version: 1field allows future schema migrations without ambiguity.
§9 — Open Questions
- Q1 — Default target convention. If
--targetomitted and registry has exactly one entry: use it (operator convenience). If multiple: error with list. Resolution: confirmed during implementation; this is the behavior unless Frank objects. - Q2 — Target validation depth.
prism configure-lanvalidates ssh connectivity. Should it also validaterepo_direxists on remote? Probably yes — costs one extra ssh round trip. Resolution: yes. - Q3 — Secrets in registry.
targets.jsondeliberately holds no secrets — only paths and IDs. API keys stay incredentials.personal.json(one set per local install). neo4j_password resolved via env-var name in registry. Resolution: confirmed; secrets stay separated by file. - Q4 —
prism_install_lanvsprism_upgrade_lanfirst-time semantics.install-lanshould be safe to run on a target that already has Prism installed (idempotent — short-circuits to upgrade). Or strict (errors if already installed). Resolution during implementation: idempotent.
§10 — Performance Consciousness
- Target registry is small (single JSON file, < 10KB even with many targets). Read-once-per-verb-call cost is negligible.
- ssh ControlMaster reuse from SPEC-051 carries forward: still one master socket per verb invocation.
- No new HTTP round-trips vs today’s
prism_deploysemantics. - CLI subcommand dispatch adds one extra layer for MCP verbs (
mcp_node/verbs → cli/ops); offset by the elimination of shell-out subprocess fork cost.
§11 — Authorship
- Author: Donna (Claude Code, session
c1d19f01) 2026-04-29. - Trigger: Frank’s correction during SPEC-054 tier 1C-redux design discussion — exact words: “this is a commercial product and we continue to take the easy way out.” The hardcoded
server1_config()andbin/prism-deploy-server1.shsmell triggered the redesign. - Implementation owner: Lafonda (install lane). Donna authors the spec and hands off via signal.
- Implementation gate: Frank’s GO. Lafonda’s PR #21 (SPEC-054 Phase 3) ships first; this spec’s implementation is a follow-up.
§12 — Relationship to Other Specs
- Supersedes SPEC-051 Phase 1 + Phase 2 multi-target hardcoding. SPEC-051 itself stays in history; the verbs it shipped (
prism_deploy,prism_clone_from) move to retirement. - Aligns with SPEC-053 (consumer-grade install consolidation, ADR-030 — currently open as PR #12). SPEC-053 covers consumer bootstrap one-liners +
prism update+ statusline wiring; SPEC-055 covers the multi-target server-side install lifecycle. Lafonda noted in her SPEC-054 ack that PR #12 will rebase onto Phase 3 + this spec. - Inherits target-registry pattern from credentials handling — same
~/.prism/location, same per-operator scope. - Consistent with
feedback_no_hardcoded_machine_values.mdandfeedback_commercial_grade_naming.md— both say zero machine-specific identifiers in source. - Does not modify
feedback_intent_is_first_class.mdposture — separate verbs per intent, not multiplexed flags.

