# PK01 Provider Operations Reference

PK01 is a Tencent Cloud compute provider attached to UniDesk through `provider-gateway` with Provider ID `PK01`. This reference is the long-term operating boundary for PK01 host access, provider-gateway bootstrap state, pikanode retention, and disk GC. General provider-gateway rules remain authoritative in `docs/reference/provider-gateway.md`; general GC safety rules remain authoritative in `docs/reference/gc.md`.

## Operating Entry Points

Use UniDesk SSH passthrough for PK01 host operations:

```bash
trans PK01 argv hostname
trans PK01 sh <<'SH'
df -h /
docker ps --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}'
SH
```

When the PK01 provider-gateway channel is offline, operators may use the owner-only break-glass SSH source `/root/.unidesk/.env/PK01_ssh.txt`. The file format is two lines: line 1 is the SSH target such as `ubuntu@<host>`, line 2 is the password. Do not print the second line, commit it, copy it into issue comments, or pass it through commands that echo typed input. Prefer an ephemeral `SSH_ASKPASS` wrapper so OpenSSH can handle `keyboard-interactive` or password prompts without exposing the value:

```bash
tmp=$(mktemp -d /tmp/pk01-askpass.XXXXXX)
chmod 700 "$tmp"
cat >"$tmp/askpass.sh" <<'SH'
#!/bin/sh
printf '%s\n' "$PK01_SSH_PASS"
SH
chmod 700 "$tmp/askpass.sh"

PK01_SSH_PASS="$(sed -n '2p' /root/.unidesk/.env/PK01_ssh.txt)" \
SSH_ASKPASS="$tmp/askpass.sh" \
SSH_ASKPASS_REQUIRE=force \
DISPLAY=none \
setsid -w ssh \
  -o StrictHostKeyChecking=accept-new \
  -o ConnectTimeout=8 \
  -o PreferredAuthentications=keyboard-interactive,password,publickey \
  "$(sed -n '1p' /root/.unidesk/.env/PK01_ssh.txt)" \
  'hostname; date -Is'

rm -rf "$tmp"
```

This fallback is for bounded host diagnostics or node-local recovery only. After restoring `unidesk-provider-gateway-pk01`, return to `trans PK01 ...` and `platform-infra ... --target PK01` as the operating truth.

Before closing an operation, verify both the provider channel and host workload state:

```bash
bun scripts/cli.ts debug health
trans PK01 sh -- 'docker inspect --format "name={{.Name}} restart={{.HostConfig.RestartPolicy.Name}} pid={{.HostConfig.PidMode}} state={{.State.Status}} image={{.Config.Image}}" unidesk-provider-gateway-pk01'
trans PK01 sh -- 'docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Status}}"'
```

PK01 has no k3s control plane. `trans PK01:k3s ...` is not an operating truth. If a future PK01 k3s lane is introduced, it must get a separate runtime-lane reference and must not reuse the current pikanode host-data policy as a Kubernetes retention policy.

## Provider Gateway Bootstrap State

PK01 currently uses a direct Docker provider-gateway deployment rather than a full UniDesk source checkout. The node-local runtime bundle is:

| Item | Path / value | Boundary |
|---|---|---|
| Provider ID | `PK01` | Must stay unique in the UniDesk node registry. |
| Container | `unidesk-provider-gateway-pk01` | Must be `restart=always`, `pid=host`, and `running`. |
| Runtime bundle | `/home/ubuntu/unidesk-provider-pk01` | Minimal workspace mounted read-only into the gateway container. |
| Env file | `/home/ubuntu/.unidesk/state/provider-pk01/provider.env` | Contains provider token and must not be printed, copied into docs, or committed. |
| Host SSH key | `/home/ubuntu/.unidesk/host-ssh-pk01/id_ed25519` | Mounted read-only at `/run/host-ssh`; public key is authorized for `ubuntu`. |
| Logs | `/home/ubuntu/.unidesk/logs/provider-pk01` | Node-local runtime logs, not a Git source of truth. |
| Host SSH target | `root@host.docker.internal:22` | `trans PK01 ...` is expected to land as root for host/systemd operations. |
| Provider-gateway egress proxy | `127.0.0.1:18789` | Provider maintenance tunnel only; do not bind Sub2API upstream accounts to this endpoint. |
| Host proxy HTTP endpoint | `127.0.0.1:10809` | PK01 host proxy endpoint for Sub2API account proxy binding, declared by `config/platform-infra/host-proxy.yaml#targets.PK01`. |

Long-term provider-gateway upgrades should converge to the standard `provider.upgrade mode=schedule` flow described in `docs/reference/provider-gateway.md`. If PK01 is still on the direct Docker bootstrap path, do not rebuild the gateway synchronously through the gateway's own `trans PK01` session. Use a detached node-local job or first move PK01 to the standard attach/upgrade bundle.

The minimal PK01 provider-gateway health contract is:

- `debug health` shows `providerId=PK01` as online.
- labels include `providerGatewayVersion`, `providerGatewayRuntimeGuardOk=true`, `providerGatewaySshDataTransport=tcp-pool`, and a nonzero ready SSH data pool.
- `trans PK01 argv hostname` reaches the Tencent Cloud host and returns the host name.

## Host Workloads

PK01 currently hosts existing Docker workloads:

| Container | Role | Protection boundary |
|---|---|---|
| `pikanode` | Public PikaPython/PikaNode service rooted at `/home/ubuntu/pikanode` | Do not delete source, `files/`, `html/download/`, `html/upload/`, certificates, or Git state without a service-owner retention decision. |
| `met_server` | Existing MET service | Treat as protected runtime unless a separate owner-approved retention plan exists. |
| `unidesk-provider-gateway-pk01` | UniDesk maintenance bridge | Must remain running; do not stop it as part of generic disk GC. |

`pikanode` mounts `/home/ubuntu/pikanode` read-write into the container. Static/generated download artifacts under `html/download/` and repository data under `files/` may be user-visible or needed by the service. They are not generic GC candidates.

## Shared Caddy Edge

PK01 may act as the public Caddy edge for several YAML-declared UniDesk services. The durable source of truth stays in the owning YAML, such as `config/platform-infra/sub2api.yaml`, `config/platform-infra/langbot.yaml`, `config/platform-infra/n8n.yaml`, and the HWLAB node/lane YAML. Do not hand-edit PK01 Caddy or FRP state as a separate routing truth.

`/etc/caddy/Caddyfile` is a shared artifact. UniDesk-managed writers must update only their own `# BEGIN unidesk managed <owner>` block, preserve other managed blocks, validate the merged file with Caddy before install, and reload Caddy only after validation succeeds. The shared helper is `scripts/src/pk01-caddy.ts`; service CLIs should call it through their owning public-exposure wrapper instead of carrying a private full-file Caddy writer.

If one public service fails while other services still work, restore the missing route through that service's YAML-controlled apply/public-exposure command, not by replacing the whole Caddyfile. A diagnostic check may list managed block markers with `grep '^# BEGIN unidesk managed ' /etc/caddy/Caddyfile` and run `sudo caddy validate --config /etc/caddy/Caddyfile`, but those checks are evidence only; durable repair belongs to the owning CLI.

In this mode, public ports `80` and `443` belong to Caddy. The existing `pikanode` container must be bound to a loopback HTTP port and used only as the apex PikaPython/PikaNode upstream. Public platform routes must be read from their owning YAML. For the current Sub2API PK01 host-Docker target, `api.pikapython.com` uses `publicExposure.mode=pk01-local` and API traffic follows `client -> PK01 Caddy -> 127.0.0.1:<YAML local upstream port> -> PK01 host-Docker Sub2API`. For k3s external-active targets, the route may instead be `client -> PK01 Caddy -> PK01 frps remote port -> target frpc -> target Sub2API`. Neither path may pass through pikanode or a master-server reverse proxy.

Caddy binary installation is also YAML-controlled. If `publicExposure.pk01.caddyDownloadProxyUrl` is set, PK01 Caddy downloads must use that proxy URL. Sub2API account-level proxy binding must use the PK01 host proxy HTTP endpoint, not the provider-gateway egress proxy.

PK01's account-level host proxy is a status-only Hysteria client connected to the NC01-local `../vpn-server` deployment. The durable declaration is `config/platform-infra/host-proxy.yaml#targets.PK01`, and protected/manual Sub2API account bindings should reference it through `config/platform-infra/sub2api-codex-pool.yaml` `manualAccounts.bindingSources.*.hostProxyRef`. Do not replace it with `127.0.0.1:18789`; verify it with `bun scripts/cli.ts platform-infra egress-proxy host status --target PK01` and then validate the Sub2API account/user request path.

The public certificate depends on DNS. The `api.pikapython.com` record must resolve to the YAML-declared PK01 public address before Caddy can complete ACME issuance. If the DNS record is absent or stale, local probes such as PK01 loopback app health checks, loopback Caddy `--resolve` checks, or target-specific FRP remote-port checks can prove parts of the data path, but final `https://api.pikapython.com` validation remains blocked until DNS is corrected.

For PK01 host-Docker Sub2API, `platform-infra sub2api status --target PK01` and `validate --target PK01` are the first-line app and edge checks. If a unified OpenAI-compatible key returns `account_select_failed` or `no available accounts` while a Sub2API WebUI account test succeeds, distinguish account test from gateway scheduling: the WebUI test loads one account directly, while gateway traffic uses the API key's group and the `account_groups` join table. Check the admin account availability view and the group membership for the target accounts, and verify public `/v1/responses` with a key fingerprint only. Do not print admin passwords, API keys, or account credentials while diagnosing this path.

## Host PostgreSQL

PK01 host-native PostgreSQL is declared by `config/platform-db/postgres-pk01.yaml` and managed through `bun scripts/cli.ts platform-db postgres plan|status|export-secrets|apply`; daily operation commands live in `$unidesk-ops` at `.agents/skills/unidesk-ops/SKILL.md`. It is a host systemd service, not a Docker container or k3s workload. The YAML is the source of truth for PostgreSQL version, TLS mode, listening addresses, `pg_hba` source CIDRs, generated Secret source files, exported `DATABASE_URL`, and backup timer settings.

The PK01 PostgreSQL cluster is a shared platform database instance. Multiple UniDesk services may be declared as separate `objects.roles[]` and `objects.databases[]` entries, with matching Secret sources, `pg_hba` hostssl rules and optional connection-string exports. A new platform consumer should use its own database and role inside this existing instance; do not create a second PostgreSQL instance or use one database with multiple schemas as the default isolation boundary for application state.

Cross-node platform consumers must connect directly to the YAML-declared `postgres.network.connectionHost`. For consumers outside the PK01 private VPC, that value must be PK01's public endpoint, not the private `10.0.8.3` address and not a master-server tunnel. The master server may run control-plane CLI operations and secret sync, but it must not become the data-plane relay for D601, G14, Sub2API, HWLAB, AgentRun, or other PostgreSQL clients.

`postgres.network.publicDns` is an optional alias for operator readability and future `sslmode=verify-full` work. With the current PostgreSQL native TLS posture, clients use `sslmode=require`; DNS resolution is therefore not a cutover blocker when `connectionHost` is a reachable IP endpoint. If `publicDns` later becomes the connection host or `verify-full` is enabled, certificate common name/SAN and DNS must be promoted back into the cutover criteria.

Exported connection strings are written under the configured ignored Secret root and must never be committed or printed in full. CLI output may show key names, presence, fingerprints, selected host, SSL status, app connection status, and source/target Secret references only. If a consumer's public egress IP changes, update the YAML `allowSources` and matching `pg_hba` rules, then use the `$unidesk-ops` PK01 Host PostgreSQL workflow to apply and recheck status.

When only a connection-string export format or consumer target changes, use `platform-db postgres export-secrets --confirm` first. That command updates local ignored Secret source/export files from YAML without restarting or reconfiguring the PK01 PostgreSQL service; the consumer-specific Secret sync command is still required afterward.

Backup coverage is explicit YAML state. Do not assume every declared database is backed up unless the backup configuration names it directly or the CLI reports multi-database coverage.

## Disk GC Policy

PK01 follows the same safe-stop principle as G14: first produce a bounded attribution, then clean only classified candidates, and stop when remaining pressure is in protected runtime data.

Default sequence for a high-water incident:

1. Run generic remote GC plan and, if useful, confirmed run:
   ```bash
   bun scripts/cli.ts gc remote PK01 plan --target-use-percent 60 --limit 100 --full
   bun scripts/cli.ts gc remote PK01 run --confirm --target-use-percent 60 --limit 100 --full
   ```
2. Inspect PK01-specific host data with short passthrough commands; avoid full-root `du` in one `trans` call because `trans` has a 60 second hard timeout.
3. For pikanode growth, clean only `html/temp` direct child directories that are older than the configured node-local retention window. Preserve direct files such as `stdout.log`, `update.log`, `accesstoken.json`, `pullrequest.json`, and any recent temp workspaces.
4. Re-check `df -h /`, provider health, Docker container state, and a pikanode local HTTPS probe.
5. If the target still cannot be reached without touching `html/download/`, `files/`, Docker images, or other protected runtime data, stop and make a retention/capacity decision instead of widening deletion scope.

PK01 pikanode temp directories are safe to remove only under this narrow definition:

- path is a direct child directory of `/home/ubuntu/pikanode/html/temp`;
- path is not a symlink;
- parent is exactly `/home/ubuntu/pikanode/html/temp`;
- mtime is older than the configured retention window;
- deletion uses `rm -rf --one-file-system` and never follows paths outside that root.

Never use `rm -rf /home/ubuntu/pikanode/html/temp/*` as an unbounded shell expansion. It risks deleting current generation workspaces and direct state/log files.

## Long-Term Retention Mechanisms

PK01 has node-local retention controls installed so that pikanode temp output and logs do not grow without bound:

| Mechanism | Node-local path | Purpose |
|---|---|---|
| pikanode temp timer | `/etc/systemd/system/unidesk-pk01-pikanode-temp-gc.timer` | Runs pikanode temp retention on a daily timer. |
| pikanode temp service | `/etc/systemd/system/unidesk-pk01-pikanode-temp-gc.service` | Executes `/usr/local/sbin/unidesk-pk01-pikanode-temp-gc` as a one-shot cleanup. |
| pikanode temp script | `/usr/local/sbin/unidesk-pk01-pikanode-temp-gc` | Deletes only old direct temp directories under the protected root. |
| retention log | `/var/log/unidesk-pk01/pikanode-temp-gc.log` | Bounded operational evidence for the timer. |
| pikanode logrotate | `/etc/logrotate.d/unidesk-pk01-pikanode` | Rotates pikanode temp/runtime logs and the retention log. |
| journald cap | `/etc/systemd/journald.conf.d/99-unidesk-pk01.conf` | Caps systemd journal growth on PK01. |

Operational checks:

```bash
trans PK01 sh -- 'systemctl status unidesk-pk01-pikanode-temp-gc.timer --no-pager'
trans PK01 sh -- 'sudo systemctl start unidesk-pk01-pikanode-temp-gc.service && tail -n 40 /var/log/unidesk-pk01/pikanode-temp-gc.log'
trans PK01 sh -- 'sudo logrotate -d /etc/logrotate.d/unidesk-pk01-pikanode'
```

The timer and logrotate configuration are node-local operational state. If a future UniDesk CLI subcommand manages PK01 retention centrally, it must first render a dry-run plan, show the same protected paths, and then install/update these node-local files through a confirmed operation.

## Space Attribution Baseline

PK01 space attribution should use short, bounded commands. Recommended probes:

```bash
trans PK01 sh -- 'df -h / && df -i /'
trans PK01 sh -- 'sudo timeout 20 du -xhd1 /var /home/ubuntu/pikanode /home/ubuntu/.vscode-server /var/lib/docker /var/log 2>/dev/null | sort -h | tail -80'
trans PK01 sh -- 'docker system df -v | sed -n "1,220p"'
trans PK01 sh -- 'sudo find /home/ubuntu/pikanode/html/temp -xdev -mindepth 1 -maxdepth 1 -printf "%TY-%Tm-%Td %TH:%TM %p\n" | sort | tail -40'
```

Interpretation guide:

| Path | Meaning | Default action |
|---|---|---|
| `/home/ubuntu/pikanode/html/temp` | Generated pikanode build workspaces | Managed by PK01 temp retention. |
| `/home/ubuntu/pikanode/html/download` | Generated ZIP downloads | Protected unless a separate download retention policy is approved. |
| `/home/ubuntu/pikanode/files` | pikanode repository/service data | Protected. |
| `/home/ubuntu/.vscode-server` | VS Code remote server, extensions, and cache | Do not delete installed servers/extensions by default; cached VSIX cleanup needs an explicit policy. |
| `/var/lib/docker` | Docker overlay/image/container state for PK01 workloads | Do not prune generically; inspect running containers first. |
| `/var/log/journal` | systemd journal | Managed by journald cap; use sudo when vacuuming manually. |

## Nginx Host-Docker 受控部署

- PK01 的 Nginx 负载均衡器由 `config/platform-infra/nginx.yaml` 管理。
- 日常部署、状态、logger API 和排障统一见 `$unidesk-nginx`。
- 部署形态与 PK01 Sub2API 一致，使用 UniDesk YAML-first 的 host-Docker Compose 入口。
- apply 默认提交异步 job；长传输、镜像加载和验证不能占用单次 SSH 长连接。
- 容器使用 host network，并限制为 `64m` 内存、`0.25` CPU 和 64 个 PID。
- 业务监听端口、上游列表、管理端口、日志上限和 logger API key 来源均来自 owning YAML。
- PK01 不构建镜像；镜像在 NC01 构建后以 SHA-256 和镜像 ID 校验的产物下发。
- `platform-infra nginx status --target PK01` 必须报告测试后端活动数为零。
