# sharekit 一键安装 — LLM/Agent Prompt v2

You are an agent tasked with installing the sharekit stack on a fresh Ubuntu 24.04 host. You will pull all source code from public URLs (no scp, no private key, no GitHub auth), install Python venv, configure nginx + Let's Encrypt via acme.sh, set up systemd services for a public file share and MCP server, and verify both endpoints respond over HTTPS.

Both source binary and documentation are already on `https://files.spannerjun.top` — start by reading:

  https://files.spannerjun.top/raw?path=docs/INSTALL.md   # full install tutorial (11 KB)

## Output contract

When you finish, you MUST be able to confirm all of the following:

1. `systemctl is-active sharekit-fileserver` → `active`
2. `systemctl is-enabled sharekit-fileserver` → `enabled`
3. `systemctl is-active sharekit-mcpserver` → `active`
4. `systemctl is-enabled sharekit-mcpserver` → `enabled`
5. `curl -ksS https://files.<your-domain>/health` → HTTP 200 with body `{"status":"ok",...}`
6. `curl -ksS https://mcp.<your-domain>/mcp` accepting POST `/initialize` JSON-RPC → 200 + `mcp-session-id` response header
7. `curl -ksS -X POST https://mcp.<your-domain>/mcp -d '{"jsonrpc":"2.0","id":1,"method":"tools/list",...}'` → 21 tools registered

If you cannot reach step 5–7, do NOT declare success. Diagnose via `journalctl -u sharekit-fileserver -n 30` and `journalctl -u sharekit-mcpserver -n 30`.

## Required inputs (ask the user if missing)

- A Linux server (Ubuntu 22.04+ or Debian 12+) with root access
- Two unclaimed DNS records pointing at the server's IP (suggest `files.<user-domain>` and `mcp.<user-domain>`)
- The domain for cert issuance (must be the same one the user owns)
- Confirm port 80 and 443 are reachable from the public internet (curl works from outside)
- For smail.icu integration (optional): the password for `root@smail.icu`

## Procedure

### Step 1 — Preflight checks

```bash
# Confirm you are root
[ "$(id -u)" -eq 0 ] || { echo "must be root, aborting"; exit 1; }

# Confirm internet
curl -ksS --max-time 5 https://files.spannerjun.top/raw?path=docs/INSTALL.md | head -1

# Confirm DNS
dig +short files.<user-domain> || echo "DNS for files.<user-domain> missing"
dig +short mcp.<user-domain>   || echo "DNS for mcp.<user-domain> missing"

# Confirm ports free
ss -tln | grep -E ':80|:443' || echo "no listeners on 80/443"
```

If any check fails, STOP and report the blocker.

### Step 2 — Set variables and download source

```bash
USER_DOMAIN="<ask the user>"             # e.g. example.com
SHARE_ROOT="/home/hermes/sharekit"
FILES_ROOT="/home/hermes/files"

mkdir -p "$SHARE_ROOT"

# Auto-discover latest tarball from the public release browse page
TARBALL=$(curl -ksS "https://files.spannerjun.top/browse?path=sharekit-release/" \
   | grep -oP 'sharekit-\d{8}-\d{4}\.tar\.gz' | sort | tail -1)
[ -n "$TARBALL" ] || { echo "could not discover tarball"; exit 1; }
echo "discovered: $TARBALL"

curl -ksS -o /tmp/$TARBALL \
   "https://files.spannerjun.top/raw?path=sharekit-release/$TARBALL"
tar xzf /tmp/$TARBALL -C /home/hermes/

ls "$SHARE_ROOT/"   # expect file_server.py, mcp_server.py, README.md, sharekit-bootstrap.sh, skill-install.sh
```

### Step 3 — Create service user and venv

```bash
id hermes >/dev/null 2>&1 || useradd --system --home /home/hermes --shell /bin/bash \
   --comment "Hermes service account" hermes

python3 -m venv /home/hermes/venv
/home/hermes/venv/bin/pip install --quiet \
   fastapi httpx 'uvicorn[standard]' pydantic mcp
chmod -R go+rX /home/hermes/venv
```

### Step 4 — Configure environment

```bash
# Pull secrets template from public source (redacted), then edit with real values
curl -ksS -o "$SHARE_ROOT/config.env.example" \
   "https://files.spannerjun.top/raw?path=sharekit-release/config.env.example"

cp "$SHARE_ROOT/config.env.example" "$SHARE_ROOT/.env"
chmod 600 "$SHARE_ROOT/.env"

# Edit .env — at minimum set:
#   SHARE_MCP_ALLOWED_HOSTS=mcp.<USER_DOMAIN>,127.0.0.1,localhost
#   SHAREKIT_PUBLIC_BASE=https://files.<USER_DOMAIN>
# For smail.icu integration, fill all SMAIL_* fields (see INSTALL.md §5).
$EDITOR "$SHARE_ROOT/.env"
```

### Step 5 — Nginx placeholder certificates + reverse-proxy config

```bash
mkdir -p /etc/nginx/ssl
for sub in files mcp; do
  openssl req -x509 -nodes -days 1 -newkey rsa:2048 \
    -keyout /etc/nginx/ssl/${sub}.${USER_DOMAIN}.key \
    -out /etc/nginx/ssl/${sub}.${USER_DOMAIN}.crt \
    -subj "/CN=${sub}.${USER_DOMAIN}"
done

# Write /etc/nginx/conf.d/files.<USER_DOMAIN>.conf
# Write /etc/nginx/conf.d/mcp.<USER_DOMAIN>.conf
# (See INSTALL.md §3.4 for full contents.)
# CRITICAL: for mcp.<USER_DOMAIN>.conf use:
#     proxy_set_header Host 127.0.0.1;     ← not $host
# This avoids FastMCP's 421 "Invalid Host header" / "Misdirected Request".
# CRITICAL: ssl_certificate must point at fullchain.crt (after LE issue),
# not single cert, or s_client warns "unable to verify".

nginx -t && nginx -s reload
```

### Step 6 — Run the public bootstrap script

```bash
sudo "$SHARE_ROOT/sharekit-bootstrap.sh"
```

This will: chown everything to `hermes:hermes`, install two systemd units from the bundled template, daemon-reload, enable+start both services, wait for `/health` to respond, and bundle local hermes skills (if present) into `/home/hermes/files/skills/`.

### Step 7 — Real Let's Encrypt certs

```bash
acme.sh --issue -d files.${USER_DOMAIN} -w /var/www/acme-challenge
acme.sh --install-cert -d files.${USER_DOMAIN} --ecc \
  --cert-file /etc/nginx/ssl/files.${USER_DOMAIN}.crt \
  --key-file /etc/nginx/ssl/files.${USER_DOMAIN}.key \
  --fullchain-file /etc/nginx/ssl/files.${USER_DOMAIN}.fullchain.crt \
  --reloadcmd "systemctl reload nginx"

acme.sh --issue -d mcp.${USER_DOMAIN} -w /var/www/acme-challenge
acme.sh --install-cert -d mcp.${USER_DOMAIN} --ecc \
  --cert-file /etc/nginx/ssl/mcp.${USER_DOMAIN}.crt \
  --key-file /etc/nginx/ssl/mcp.${USER_DOMAIN}.key \
  --fullchain-file /etc/nginx/ssl/mcp.${USER_DOMAIN}.fullchain.crt \
  --reloadcmd "systemctl reload nginx"

nginx -s reload
```

### Step 8 — Verification (do not stop until all 4 pass)

```bash
echo '== 1. file_server health =='
curl -ksS https://files.${USER_DOMAIN}/health

echo '== 2. file_server browse =='
curl -ksS -o /dev/null -w '%{http_code}\n' https://files.${USER_DOMAIN}/

echo '== 3. mcp_server init =='
SID=$(curl -ksS -i -X POST https://mcp.${USER_DOMAIN}/mcp \
   -H 'Content-Type: application/json' \
   -H 'Accept: application/json, text/event-stream' \
   -d '{"jsonrpc":"2.0","id":1,"method":"initialize",
        "params":{"protocolVersion":"2025-03-26","capabilities":{},
                 "clientInfo":{"name":"probe","version":"0"}}}' \
   | grep -i '^mcp-session-id:' | tr -d '\r' | awk '{print $2}')

[ -n "$SID" ] && echo "MCP init OK, session=$SID" || { echo "MCP init FAIL"; exit 1; }

echo '== 4. mcp tools/list =='
curl -ksS -X POST https://mcp.${USER_DOMAIN}/mcp \
   -H 'Content-Type: application/json' \
   -H 'Accept: application/json, text/event-stream' \
   -H "mcp-session-id: $SID" \
   -d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' \
   | python3 -c "import json,sys,re; d=sys.stdin.read(); m=re.search(r'data: (.*)', d); print(len(json.loads(m.group(1))['result']['tools']), 'tools') if m else print('parse fail')"

echo '== 5. systemd =='
systemctl is-active sharekit-fileserver sharekit-mcpserver
systemctl is-enabled sharekit-fileserver sharekit-mcpserver
```

## Failure modes → fix table

| Symptom | Cause | Fix |
|---|---|---|
| 421 on `mcp.<domain>` | FastMCP rejects Host header (not on allowlist) | Confirm nginx `proxy_set_header Host 127.0.0.1;` (literal, not $host) and `.env` has `SHARE_MCP_ALLOWED_HOSTS=mcp.<domain>,...` |
| `mcp 400 Bad Request` | missing `mcp-session-id` header between calls | reuse SID returned from initialize |
| 502 | mcp server hasn't loaded — wait 30+ seconds after `systemctl start` | `journalctl -u sharekit-mcpserver -n 30` |
| ssl verify fail in browser | nginx using single cert, not fullchain | point `ssl_certificate` at `*.fullchain.crt` |
| 521 in nginx log | "Misdirected Request" — upstream ssl mismatch | same as 421 — Host header is wrong |
| `smail_*` tools time out | SSH link to smail.icu not configured | install `/home/hermes/.ssh/config` with `Host smail-server` → `HostName smail.icu`; create `/usr/local/bin/smail-askpass.sh` |
| `smail_*` Permission denied | SSH_ASKPASS not loaded | ensure mcp_server started by hermes (not root), `/usr/local/bin/smail-askpass.sh` exists, `/home/hermes/.ssh/config` readable |
| `connection refused 8789/8790` | systemd service down | `systemctl status sharekit-{fileserver,mcpserver}`; re-run `sharekit-bootstrap.sh` |

## When done — report to user

Print:
- Final service URLs (https://files.<USER_DOMAIN>, https://mcp.<USER_DOMAIN>/mcp)
- Confirm 21 MCP tools registered (12 sharekit + 9 smail)
- Confirm systemd units active + enabled
- Mention any optional next steps: smail integration if user wants mail, expose via mesh tool

Do NOT report success unless all verification steps in §8 passed.

---

**Source-of-truth references** (treat as canonical):

- Live instance URL: https://files.spannerjun.top
- Live MCP URL: https://mcp.spannerjun.top/mcp
- Source of truth for INSTALL.md: https://files.spannerjun.top/raw?path=docs/INSTALL.md
- Source release directory: https://files.spannerjun.top/browse?path=sharekit-release/
- smail skill file: https://files.spannerjun.top/raw?path=skills/smail-mail-SKILL.md

If the user objects to anything, ask before proceeding. If you must deviate, document the deviation in your final report.
