# NEW SERVER BOOTSTRAP + ROOT MCP RUNBOOK

Related portability/backup standard: `docs/JEFFREY_MCP_BRIDGE_PORTABILITY_ANCHOR.md`.

Date: 2026-09-26
Status: CANONICAL BOOTSTRAP RUNBOOK

## Purpose

This document defines how the clean replacement server is audited, prepared and migrated through the authenticated ChatGPT MCP control plane.

The immediate goal is not to build a long-term unrestricted administration API. The immediate goal is to give the migration controller enough temporary root capability to inspect and configure an otherwise clean server correctly, migrate DATASET/SOLO/Jeffrey, verify the result, and then reduce privileges after bootstrap.

## Two independent permission layers

There are two different permission systems and they must not be confused.

### 1. ChatGPT plugin permission policy

The ChatGPT setting `Allow all tools` means ChatGPT may invoke tools exposed by the MCP application without asking the user for approval on every individual tool call.

It does NOT itself grant Linux root privileges.

### 2. Linux/MCP execution privilege

Actual operating-system power is determined by what the MCP server exposes and which Unix identity executes the operation.

Examples:

- MCP exposes only `bridge_ping` -> ChatGPT can only ping, even if `Allow all tools` is enabled.
- MCP exposes `root_exec` and that implementation executes as root -> ChatGPT effectively has root capability through that tool.
- MCP exposes a fixed allowlist running as `aiops` -> ChatGPT has only that allowlisted capability.

Therefore the intended bootstrap combination is:

`ChatGPT Allow all tools -> OAuth-authenticated MCP -> audited temporary root_exec -> Linux root`

After migration/bootstrap the intended steady-state combination is narrower:

`ChatGPT -> OAuth-authenticated MCP -> specialized tools / sudo allowlist -> dedicated service users`

## Current control-plane state

Verified 2026-09-26:

- public MCP endpoint: `https://tesh.combatsolo.info/mcp`;
- local MCP listener: `127.0.0.1:8766`;
- systemd service: `jeffrey-mcp.service`;
- MCP transport: Streamable HTTP;
- OAuth protection is active;
- protected-resource metadata is published;
- OAuth authorization-server metadata is published;
- dynamic client registration works;
- authorization-code + PKCE S256 works;
- refresh token support is enabled;
- unauthenticated `/mcp` returns HTTP 401 + `WWW-Authenticate`;
- ChatGPT successfully authenticated and invoked `bridge_ping`;
- current MCP mode: `oauth-bootstrap-readonly`;
- current MCP version: `0.2-mcp`.

A compatibility correction was required for `mcp==1.26.0`: `AuthorizationCode` and `RefreshToken` do not provide `.subject`; the private single-owner provider therefore uses the fixed local owner identity.

## Bootstrap root tool

For the clean-server preparation stage, introduce one temporary high-capability MCP operation named `root_exec`.

Important implementation detail discovered after OAuth bootstrap: the current FastMCP contract exports only `bridge_ping`, and `jeffrey-mcp.service` was originally created with `User=nobody`, `Group=nogroup`, `NoNewPrivileges=true`. Therefore ChatGPT `Allow all tools` alone cannot provide Linux administration. Bootstrap root requires BOTH: (1) register `root_exec` with `@server.tool()` in the FastMCP server, and (2) provide that tool a root-capable execution context. For the temporary clean-server bootstrap phase the simplest model is to run `jeffrey-mcp.service` as root with an explicit audit log; after migration this must be reduced/replaced as described in Phase F.

Required behavior:

- execute through a root-owned control process or an equivalent root-capable path;
- accept command/argv plus a bounded timeout;
- return exit code, stdout and stderr;
- record timestamp, authenticated MCP principal, requested command, exit code and duration in an append-only audit log;
- never echo OAuth credentials, bridge tokens, exchange keys or stored application secrets by default;
- enforce output-size limits;
- use explicit timeouts so a broken command cannot strand the MCP worker indefinitely;
- preserve the existing `bridge_ping` as a simple control-plane health check;
- keep a clear rollback path to the previous MCP version.

During bootstrap, arbitrary root execution is intentionally permitted because the server is being built and migrated. It is temporary infrastructure, not the final application security model.

## Root safety invariant

`root_exec` is powerful enough to destroy the server. Therefore the migration controller follows these operating rules:

1. Inspect before changing.
2. Back up config before replacing it.
3. Validate generated config before reload/restart where a validation command exists (`nginx -t`, PHP config checks, systemd verification, etc.).
4. Do not repartition, recreate RAID, format filesystems or alter boot storage until the complete block-device topology has been captured and understood.
5. Do not use `mdadm --create` merely because the target architecture mentions RAID.
6. Do not replace an ISPmanager-generated vhost when an include/drop-in can achieve the same result.
7. Prefer reversible changes during bootstrap.
8. Record material changes in migration docs.
9. Do not migrate old exchange/API secrets into the clean Jeffrey runtime.
10. Never run old-host and new-host active writers against the same logical runtime unless dual-writer behavior is explicitly designed.

## Phase A — immutable baseline audit

Before tuning or installing application components, capture a baseline of the new server.

### Identity / OS / boot

Capture:

- hostname;
- Debian release and exact point version;
- kernel;
- boot mode (UEFI/legacy);
- uptime;
- timezone and NTP state;
- installed ISPmanager version/components;
- currently enabled systemd units relevant to hosting.

### CPU / memory

Capture:

- CPU model, topology, cores/threads, virtualization flags;
- NUMA topology if applicable;
- total/available RAM;
- swap configuration;
- memory pressure / obvious errors.

Do not tune kernel/network parameters based on generic hosting recipes until actual workload and hardware are known.

### Storage / RAID — highest caution

Capture before making any storage change:

- `lsblk` topology including model, serial, size, filesystem and mount points;
- `/proc/mdstat`;
- `mdadm --detail --scan` where available;
- `mdadm --detail` for every active md device;
- filesystem types and UUIDs;
- `/etc/fstab`;
- mount options;
- SMART/NVMe health where supported;
- RAID member state, degraded/rebuild state, bitmap/resync status;
- current partition tables;
- root/boot/EFI arrangement.

Known fact already observed: root filesystem is on `/dev/md2` at roughly 934 GiB. This means RAID/software-md already exists and MUST be inspected before any decision to rebuild/change it.

Decision gate after storage audit:

- if existing RAID layout is healthy and appropriate, preserve it;
- if degraded, diagnose member/device health before repair;
- if topology is materially wrong for the target workload, document a deliberate rebuild plan before touching partitions/filesystems;
- do not optimize RAID chunk/filesystem layout after data migration has begun unless there is a strong measured reason.

### Network

Capture:

- physical/logical interfaces;
- IPv4/IPv6 addresses;
- default route and gateway;
- DNS resolver configuration;
- MTU;
- listening ports;
- firewall/nftables state;
- Cloudflare-facing origin expectations;
- reverse DNS where relevant.

Known current facts: interface `eno1`, IPv4 `116.202.234.52`, IPv6 `2a01:4f8:241:4d2d::2`, default IPv4 gateway `116.202.234.1`.

Do not repeat the earlier `ens2`/`eno1` configuration mismatch. The live interface name observed on the final install is canonical.

### Hosting stack

Capture:

- nginx version and compiled modules;
- active ISPmanager vhost layout/includes;
- PHP versions installed;
- PHP-FPM pools and extensions;
- Composer status;
- TLS certificates and renewal mechanism;
- cron/systemd timers;
- logrotate;
- file descriptor/process limits relevant to services.

Known current hosting choice:

- nginx enabled;
- PHP-FPM FastCGI intended;
- Apache not used;
- no generic MySQL/PostgreSQL/mail/FTP requirement for Jeffrey/DATASET;
- Cloudflare provides DNS, therefore no local authoritative DNS server is required.

## Phase B — determine what the server actually needs

Optimization is requirement-driven, not package-count-driven.

Target workload consists of:

1. DATASET central data/control service;
2. SOLO Research Plane + LAB modules;
3. Jeffrey user engine / trading runtime;
4. nginx/TLS public surfaces;
5. filesystem/history/research storage;
6. controlled jobs via systemd/cron;
7. MCP administration plane.

For each workload determine:

- CPU intensity;
- memory working set;
- small-file vs sequential-I/O behavior;
- write frequency;
- latency sensitivity;
- retention/history volume;
- concurrency;
- network egress/ingress pattern;
- recovery priority.

Only after this audit decide on:

- swap size/policy;
- filesystem/mount tuning;
- PHP-FPM worker limits;
- nginx worker/open-file limits;
- systemd limits;
- log retention;
- backup cadence;
- storage separation if actually useful;
- monitoring thresholds.

Avoid cargo-cult kernel tuning. Every non-default tuning change should have a workload reason or measured symptom.

## Phase C — base server preparation

After audit and decisions:

- apply Debian updates required for the migration baseline;
- verify time sync;
- install only required utilities/packages;
- establish service users (`dataset`, `solo`, `jeffrey`, later `aiops`/bridge as appropriate);
- create target directory ownership model;
- configure nginx/PHP-FPM only for actual application requirements;
- configure log rotation;
- configure firewall to expose only intended public/admin services;
- verify TLS and Cloudflare origin behavior;
- create backup/snapshot strategy before application migration;
- capture a post-bootstrap baseline.

## Phase D — migration order

Do not perform a monolithic server clone.

Canonical order:

1. bootstrap/audit new server;
2. DATASET;
3. SOLO Research Plane + LAB modules;
4. dependent/support domains one at a time;
5. Jeffrey current active contour last among core dependencies;
6. Jeffrey clean `2.1.0` runtime starts with LIVE OFF / NO KEY;
7. qualify DEMO/DATASET integration;
8. LIVE REAL;
9. LIVE SHADOW/LIVE_SIM;
10. broader UI cleanup;
11. maintenance automation.

## Phase E — migration evidence

For every migrated contour record:

- source host/domain/path;
- destination host/domain/path;
- pre-copy start/end;
- writer freeze point when required;
- final delta sync;
- file count/size/hash checks where practical;
- services/jobs recreated;
- secrets explicitly excluded;
- ownership/permissions;
- old writer disabled/new writer enabled;
- local verification;
- public verification after DNS cutover;
- rollback path;
- final status.

## Phase F — reduce privileges after bootstrap

Full root is temporary.

After server build + domain migrations are stable:

- review the MCP audit log and identify recurring operations;
- replace arbitrary `root_exec` for normal work with specialized tools;
- move routine file/application operations under dedicated users;
- allow only specific sudo/systemd/nginx operations where root remains required;
- preserve an emergency root path only if deliberately desired;
- rotate/bootstrap credentials as appropriate;
- keep OAuth on the MCP control plane;
- keep ChatGPT permission policy independent from Linux privilege design.

This produces the intended end state: high autonomy for normal operations without leaving a permanent unrestricted remote root shell as the everyday interface.

## Immediate next action

In the MCP-enabled ChatGPT session:

1. add the temporary audited root-capable execution tool;
2. set ChatGPT plugin permission to allow tool use without per-command confirmation if the user wants autonomous migration;
3. run Phase A only — no destructive storage/network changes;
4. write the audit findings back into this runbook / migration anchor;
5. make explicit server-optimization decisions from observed hardware/workload;
6. execute Phase C;
7. begin DATASET migration only after the server baseline is declared ready.
