KeiSeiKit-1.0/skills/vm-provision/phase-2-plan.md
Parfii-bot 0be354a920 KeiSeiKit-public — clean state
Single-commit clean baseline after security scrub of niche-tells,
project codenames, internal jargon, and contributor-email leaks.

Contents:
- 100 Rust crates (_primitives/_rust/)
- 37 agent manifests (_manifests/) + generated specs (_generated/)
- 67 user-invocable skills (skills/)
- 33 hooks (hooks/)
- Composition blocks (_blocks/)
- Documentation (docs/, README.md)
- TS adapter packages (_ts_packages/)
- Assembler (_assembler/)
- Roles (_roles/)
- Templates (_templates/)
- Forgejo CI (.forgejo/)

Author: Denis Parfionovich <info@greendragon.info>

License: see LICENSE.
2026-05-01 12:09:03 +08:00

137 lines
3.6 KiB
Markdown

# Phase 2 — Plan Mode Doc
> Goal: produce a written, user-approved plan (RULE 0.5) that enumerates
> every apt change to the VM before any packet leaves the workstation.
> **Verify criterion:** user clicked "approve" on the plan; plan artefact
> exists at `<run-dir>/plan.md`.
---
## 2.a — Synthesise the plan
Write `<run-dir>/plan.md` (where `<run-dir>` is `./.keisei/vm-provision/<timestamp>/`) with
EXACTLY these sections — no more, no less:
```markdown
# VM-Provision Plan — <timestamp>
## Intent
<INTENT one-line>
## Target
- Provider: <PROVIDER>
- Region: <REGION>
- Plan: <PLAN> (<arch>)
- VM name: kei-<env>-<role> # derived, ASK if ambiguous
## Access
- Admin user: <ADMIN_USER> # default keiadmin
- SSH port: <SSH_PORT> # default 22
- SSH pubkey: <path> # read from ~/.ssh/id_*.pub
## Ports to allow (ufw + provider cloud firewall)
<APP_PORTS list>
## TLS
- Host: <TLS_HOST or none>
- Method: <HTTP-01 | DNS-01 | none>
## Hardening steps (harden-base.sh)
- apt update + upgrade
- install: ufw fail2ban unattended-upgrades needrestart auditd audispd-plugins
- write /etc/ssh/sshd_config.d/99-kei.conf
- ufw default-deny-in + rate-limit ssh + allow APP_PORTS
- fail2ban sshd jail
- auditd baseline ruleset (/etc/audit/rules.d/99-kei.rules)
- unattended-upgrades (AUTO reboot = FALSE)
## Verification (hard gate before handoff)
- ssh-check → exit 0
- firewall-diff (intent YAML vs live ufw) → exit 0
## Rollback
- `_primitives/provision-<provider>.sh destroy <VM_NAME>` — 1-command destroy.
- TF state: <path or "none CLI-driven">
## Cost estimate
<Plan price per month from PROVIDER pricing page; CITE>
```
Cite the source for every price/region/plan detail. Numbers NOT cited =
NO-GO per RULE 0.4.
---
## 2.b — Build the `firewall-intent.yaml`
Write `<run-dir>/firewall-intent.yaml`:
```yaml
default:
incoming: deny
outgoing: allow
routed: deny
rules:
- port: <SSH_PORT>
proto: tcp
action: limit
from: any
comment: "ssh (rate-limited)"
# one entry per APP_PORTS:
- port: 443
proto: tcp
action: allow
from: any
```
This file is the **source of truth** the Phase 5 `firewall-diff` will
compare against live `ufw status numbered` output. Drift = Phase 5 fail.
---
## 2.c — AskUserQuestion (customise ports, TLS, admin name)
One `AskUserQuestion` call with up to 4 questions:
1. **Admin user?** (stored as `ADMIN_USER`)
- `keiadmin` (default)
- Custom (user types — only free-text in Phase 2)
2. **SSH port?** (stored as `SSH_PORT`)
- `22` (default; simpler)
- `2222` (obscurity; not security, but reduces log noise)
- Custom
3. **Application ports to open?** (multi-select, stored as `APP_PORTS`)
- `443/tcp` — HTTPS (most apps)
- `80/tcp` — HTTP (only if ACME HTTP-01 or redirect)
- `none` — tunneled via Tailscale / private net only
4. **TLS?** (stored as `TLS_HOST` + method)
- Caddy HTTP-01 (need 80/tcp + 443/tcp + DNS pointing to VM)
- Caddy DNS-01 (no port 80 needed; need DNS provider API token)
- None (app provides its own TLS or is behind a proxy)
---
## 2.d — Present the plan for approval
Render `plan.md` in chat. Ask ONE final AskUserQuestion:
**Proceed with this plan?**
- Approve → Phase 3.
- Iterate → loop back to 2.c with the user's change request.
- Abort → emit plan-only artefact and exit (`HANDOFF_TO=none`).
---
## 2.e — Verify criterion
- [ ] `plan.md` written to `<run-dir>/plan.md`.
- [ ] `firewall-intent.yaml` written to `<run-dir>/firewall-intent.yaml`.
- [ ] User clicked "Approve".
Emit:
`Phase 2 done: plan @ <run-dir>/plan.md. <len(APP_PORTS)> ports, TLS=<method>.`
Proceed to Phase 3.