# SERVER MIGRATION ANCHOR

Date: 2026-09-25
Status: CANONICAL MIGRATION PLAN / DOMAIN-BY-DOMAIN

## 2026-09-26 migration update

- datasetis.info public DNS is cut over to the new host 116.202.234.52.
- New DATASET public HTTPS is valid via Let’s Encrypt and /api/v1/health.php returns HTTP 200 with ok=true, source_ok=true, storage OK.
- Parser2/new-host runtime is active; Gap Repair may fill a deliberately acceptable short migration gap.
- Old DATASET/source server is retained for remaining file transfer, rollback evidence and later audited cleanup; it must not be bulk-deleted.
- Canonical project documentation has moved from old-server www/docs to https://docs.datasetis.info/.
- Old-server www/docs remains preserved as migration/archive evidence until cleanup is explicitly classified and approved.


## Core decision

The move to the new server is NOT a monolithic full-server clone.

Migration is performed domain by domain / contour by contour, with a separate verification gate after each domain.

The first infrastructure step on the clean server is a temporary guarded bootstrap bridge with sufficient root/sudo capability for system setup. Once that bridge is verified, the remaining server audit, installation, file transfer, service setup and validation can be driven through the bridge.

The user retains the external DNS cutover step in Cloudflare: after a domain is verified on the new host, the user changes that domain's DNS/IP to the new server on instruction.

## Migration priority

DATASET is the first APPLICATION contour to migrate because Jeffrey depends on DATASET contracts and channels. It is NOT the next step while the new host is still only a minimal ISPmanager `Web server (WWW)` installation.

Recommended high-level order:

1. Bootstrap bridge on the clean server.
2. Inventory old and new hosts.
3. Complete the new host BASE SERVER BUILD and declare `READY FOR APPLICATIONS`: OS updates/utilities, storage monitoring, firewall/access, users/directories/ownership, nginx/ISPmanager layout, PHP/PHP-FPM runtime and exact required extensions, Composer only if required, systemd/cron runner model, logging/logrotate, backup/restore path, TLS/Cloudflare origin behavior, and service/resource limits.
4. `datasetis.info` / DATASET contour.
5. `tredersolo.online` / SOLO Research Plane, with LABs systematized as modules under the same research architecture rather than preserved as scattered standalone domains.
6. Other dependent/support domains one at a time after DATASET/SOLO are stable.
7. Jeffrey active host / `topwebclub.top/combatsolo/` only after its DATASET dependencies are verified on the new server.
8. Jeffrey clean baseline becomes `2.1.0`.
9. Bring up/qualify LIVE REAL + LIVE SHADOW before broad UI/component cleanup.

Exact ordering of secondary domains is finalized from the live inventory, not guessed in advance.

## Step 0 — bootstrap bridge

Canonical detailed runbook for the temporary full-root bootstrap phase: `docs/NEW_SERVER_BOOTSTRAP_ROOT_MCP_RUNBOOK.md`.

Canonical bridge portability/backup/restore standard: `docs/JEFFREY_MCP_BRIDGE_PORTABILITY_ANCHOR.md`. Treat the MCP bridge as a versioned infrastructure component independent from DATASET/SOLO/Jeffrey application versions; maintain both a portable no-secrets deployment bundle and a separately protected disaster-recovery backup path.

Verified storage audit: `docs/NEW_SERVER_STORAGE_AUDIT_2026-09-26.md`. Current decision: both Samsung PM981a 1 TB NVMe devices are `WARNING / endurance exceeded, drive still operational`, not `replacement required`; RAID1 arrays md0/md1/md2 are clean `[UU]`, failed=0, degraded=0, mismatch_cnt=0, media/data integrity errors=0, Available Spare=100%. Keep the existing RAID layout unchanged, use enhanced monitoring/off-host backups, avoid unnecessary write-heavy backfill, and treat this host as a temporary migration/production host rather than final long-term storage. It defines the distinction between ChatGPT permission policy and Linux privilege, the temporary audited `root_exec` model, the non-destructive hardware/RAID/network/hosting audit, workload-driven optimization decisions, migration evidence requirements, and the post-migration privilege-reduction plan.

STATUS 2026-09-26: BOOTSTRAP BRIDGE SERVER SIDE IS UP AND VERIFIED.

Verified new-host facts:
- host IPv4: `116.202.234.52`;
- OS: Debian GNU/Linux 13.5 (trixie), kernel `6.12.94+deb13-amd64`;
- root filesystem: `/dev/md2`, ~934G;
- ISPmanager is installed and available on port 1500;
- nginx is installed through ISPmanager;
- bridge HTTPS endpoint: `https://tesh.combatsolo.info/bridge/`;
- local bridge listener: `127.0.0.1:8765`;
- systemd service: `jeffrey-bridge.service`;
- authentication: Bearer token stored only on the new host in `/etc/jeffrey-bridge/token` (token value must not be documented or copied into project/chat);
- local authenticated `/health` returned HTTP 200;
- public HTTPS authenticated `/bridge/health` returned HTTP 200;
- public request without token returns HTTP 401, confirming the proxy reaches the bridge and auth gate is active.

On the clean Debian + ISPmanager host, use one initial manual root shell session to install a temporary guarded administration bridge.

MCP control-plane status (2026-09-26): a separate MCP sidecar is running as `jeffrey-mcp.service`, listening on `127.0.0.1:8766` and exposed by nginx at `https://tesh.combatsolo.info/mcp`. OAuth 2.1 protection is now active and verified end-to-end: protected-resource metadata + authorization-server metadata are public; DCR works; authorization-code + PKCE S256 works; refresh-token support is advertised; unauthenticated `/mcp` returns HTTP 401 with `WWW-Authenticate`; ChatGPT authenticated successfully and invoked `bridge_ping`. The `bridge_ping` mode string `oauth-bootstrap-readonly` is now stale/misleading: a `root_exec` tool has been exported and independently verified from ChatGPT to execute successfully as `uid=0(root)`, user `root`, cwd `/opt/jeffrey-mcp`, rc=0. A provider compatibility fix was required for `mcp==1.26.0` because `AuthorizationCode`/`RefreshToken` do not have a `.subject` attribute. Treat the MCP-enabled chat as the active migration-control chat with temporary full-root bootstrap capability.

Documentation invariant for every migration stage: record source path/domain, destination path/domain, pre-copy/freeze/final-delta sequence, file counts and checksums where practical, secrets excluded, writer ownership after cutover, public verification result, rollback state, and any deliberate deviations. Update `docs/NEXT_CHAT_HANDOFF.md` after each major stage so a fresh chat can resume without reconstructing context.

Target properties:

- HTTPS endpoint on a dedicated subdomain or protected endpoint;
- strong token and/or mTLS authentication;
- complete audit log;
- root/sudo capability sufficient for bootstrap;
- system inspection, package installation, systemd, nginx, PHP-FPM, filesystem, permissions, logs and Jeffrey/DATASET deployment;
- no exchange/API secrets embedded in the bridge;
- after migration, reduce bridge privileges/allowlist for normal operation.

Do not expose the root password through the project or chat.

## Domain-by-domain cutover model

Each domain is treated as an independent migration unit.

For every domain:

1. Inventory code, storage, runtime writers, cron/systemd jobs, nginx/vhost, PHP version/extensions, permissions and external dependencies.
2. Classify files into active runtime, immutable/history/archive, secrets, stale/temporary data.
3. Pre-copy large immutable/history data while the old domain remains live.
4. Deploy current active code/config to the new host.
5. Recreate only required services/jobs.
6. Run local/new-host checks without changing public DNS.
7. At final cutover, stop/freeze writers for that domain only where required.
8. Perform final delta sync.
9. Verify counts, sizes, hashes and critical state/contracts.
10. Ensure the new host is the only writer for that migrated domain.
11. User changes the domain/IP in Cloudflare.
12. Verify public HTTPS, routing, API/runtime freshness and downstream consumers.
13. Keep the old contour available briefly as read-only fallback with its writers disabled.
14. Only after the domain is stable move to the next one.

Never run old and new active writers against the same logical runtime without an explicit dual-writer design.

## Research preservation invariant

Cleanup on the new server must simplify topology without losing research evidence.

For SOLO/LAB migration:

- inventory all experiment modules, tools, manifests, reports, frozen datasets, result stores and human conclusions;
- classify each item as `ACTIVE_RESEARCH`, `RESEARCH_ARCHIVE`, `DUPLICATE`, or `DISPOSABLE_RUNTIME`;
- preserve provenance from old domain/path/engine id to the new location;
- checksum retained artifacts before and after migration;
- keep failed/negative experiments when they are part of the decision history;
- deduplicate only when the retained canonical copy and hash are explicitly recorded;
- never promote archived research runtime into active Jeffrey trading state.

The target is a cleaner SOLO Research Plane with no silent loss of experiments or evidence.

## DATASET first

DATASET is the first major migration contour.

The DATASET migration must preserve current contracts and data semantics, including as applicable:

- parser pipelines and current market caches;
- historical/raw archives required by current jobs;
- Coin Passport/current-history/runtime where currently used;
- orderbook/history inputs where currently used;
- live-engine release/read/health/feedback channels used by Jeffrey;
- release history and migration-critical docs;
- cron/systemd scheduling and lock semantics.

DATASET must be verified from both perspectives:

1. internally: parsers/jobs/storage/API are healthy on the new host;
2. externally: Jeffrey can read the expected DATASET contracts/channels from the migrated domain.

Do not migrate obsolete or unused historical runtime into active state merely because it exists in backup.

## Jeffrey migration

Current Jeffrey `2.0.x` remains the stable migration baseline until the move.

Jeffrey portability is now explicitly verified in code:

- `DatasetRuntimeClient` uses configurable `dataset_control.base_url`, defaulting to `https://datasetis.info`;
- runtime passport/profile reads use authenticated HTTPS with TLS verification and the engine token;
- Jeffrey therefore does not need DATASET to be installed on the same server as a hard requirement;
- however, the current `DatasetShadowLeagueConsumer` still probes a sibling `../datasetis.info` filesystem tree first for SHADOW/library data and only then falls back to HTTPS;
- this same-host shortcut must be removed/disabled in the clean Jeffrey 2.1.0 baseline so the user engine is truly host-independent and DATASET is accessed only through the supported remote contract.

On the new host:

- deploy a clean Jeffrey `2.1.0` code/runtime baseline;
- do not migrate old API keys/secrets;
- do not activate old runtime/telemetry clutter;
- retain needed history as archive/read-only evidence;
- create a fresh telemetry/runtime epoch;
- LIVE starts OFF / NO KEY;
- keys are entered manually after migration;
- DEMO and DATASET integration are verified before LIVE qualification;
- LIVE REAL + LIVE SHADOW are the first major development/qualification priority after the clean baseline.

## No database dependency

Jeffrey/DATASET current active architecture is file/storage based. Do not install or migrate MySQL/MariaDB for Jeffrey merely as part of a generic hosting checklist.

If ISPmanager itself installs or requires a database internally, treat that as ISPmanager infrastructure, not as a Jeffrey application dependency.

## Secrets

Do not copy exchange/API keys from the old host into the clean Jeffrey runtime.

Secret-bearing files discovered in backup must be classified and excluded from the clean seed. New keys are entered manually on the new host after the key-storage/validation path is verified.

## Cloudflare responsibility

The intended operational split is:

- bridge-enabled server work: inspection, install, deploy, sync, verification, writer control and post-cutover checks;
- user: change the requested domain's DNS/IP in Cloudflare when the domain is declared ready for cutover.

Do not switch multiple domains at once unless they have been explicitly verified as one inseparable dependency group.

## Rollback principle

Rollback is domain-local.

If a newly migrated domain fails verification after DNS cutover:

- stop new-host writers for that domain if needed;
- restore DNS to the old host;
- re-enable only the required old-host writers after state consistency is checked;
- investigate before attempting the domain again.

This is safer than a single all-or-nothing server migration.

## Source-of-truth boundaries

- Current Jeffrey code/runtime is canonical for current Combat behavior.
- Current DATASET code/contracts serving Jeffrey are canonical for DATASET behavior.
- `topwebclub.top/combatsolo/` is the current active Jeffrey-facing surface and the canonical source for current Combat behavior.
- the DOMAIN `combatsolo.info` is currently free/reserved and may be used later for a clean Jeffrey copy/instance on the new server;
- old code/runtime snapshots historically associated with `combatsolo.info` are reference-only and must not be promoted into the clean Jeffrey runtime merely because the domain is reused.

