Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions docs/README.md
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
# Operator docs (hand-written)

| Doc | Topic |
| --- | --- |
| [subactor-deployment.md](subactor-deployment.md) | Subactor / sub.actor / `*.subactor.com` topology via redeploy |
| [fleet.md](fleet.md) | Fleet & device registry |
| [patterns.md](patterns.md) | Common migration patterns |
| [observe.md](observe.md) | Observe mode |
| [dsl-migration.md](dsl-migration.md) | DSL migration notes |

<!-- code2docs:start --># redeploy

![version](https://img.shields.io/badge/version-0.1.0-blue) ![python](https://img.shields.io/badge/python-%3E%3D3.11-blue) ![coverage](https://img.shields.io/badge/coverage-unknown-lightgrey) ![functions](https://img.shields.io/badge/functions-985-green)
Expand Down
211 changes: 211 additions & 0 deletions docs/subactor-deployment.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
# Subactor deployment through `semcod/redeploy`

HOME: `semcod` (`redeploy`, toolkit).
Runtime facts and authority: **ADOPT** from Subactor
(`platform/config/deployment-bindings`, `www-sub-actor/docs/deployment.md`,
knowledge entries). Host fitness: **ADOPT** `wellmanifest/policy-dsl`
(`subactor.host/production-server/v1`, `subactor.host/rpi5/v1`).

This document answers: **how Subactor is deployed today**, and **how operators
drive that topology with `redeploy`** (detect → plan → apply / fleet).

> Status observed: **2026-08-15**. Live public `*.subactor.com` and current
> `sub.actor` publish path are **Plesk**. Full Docker VPS + Traefik for
> Platform/`control.sub.actor` is the **documented target** in
> `www-sub-actor/docs/deployment.md`, not yet the live Founder Control host.

---

## 1. Topology (as-is vs target)

```text
┌─────────────────────────────────────┐
│ Plesk 217.160.250.222 (edge) │
Internet ──────────────►│ DNS / TLS / mail / static / PHP │
│ Node Toolkit (founder origin) │
└──────────────┬──────────────────────┘
┌─────────────────────────────┼─────────────────────────────┐
│ │ │
*.subactor.com founder.subactor.com sub.actor
static / PHP sites Node proxy → :19081 Plesk httpdocs
(SFTP plesk:// sync) SSH -R from Lenovo (binding:
→ Caddy :18081 → Control deployment:sub-actor:production)

TARGET (policy-aligned Docker VPS — FORBID Compose inside Plesk):

Internet ──► Docker VPS (Traefik ACME DNS-01)
├─ https://sub.actor / *.sub.actor (portal image sha-*)
└─ https://control.sub.actor (Platform Control router)
Plesk remains DNS/TLS/mail/WWW edge only
```

### Domain map (SSOT)

Source of truth: `subactor/platform/config/deployment-bindings/registry.v1.json`
and `public-pages.json`. Provider for listed sites today: **`plesk`**.

| Domain | Role today | Deploy mechanism |
| --- | --- | --- |
| `subactor.com` | Marketing / main site | Plesk SFTP sync |
| `www.subactor.com`, `docs.subactor.com`, `docs-stage.subactor.com` | Docs / www | Plesk SFTP |
| `logo.subactor.com`, `status.subactor.com`, `contracts.subactor.com`, … | Brand / status | Plesk SFTP |
| `identity.subactor.com`, `chat.subactor.com` | Surface placeholders | Plesk SFTP |
| `auth.subactor.com` | Auth **bootstrap** static site (not full IdP yet) | Plesk SFTP; project `projekty/auth-subactor-com` |
| `founder.subactor.com` | Public Founder Control origin | Plesk Node `plesk-origin` + SSH reverse tunnel |
| `sub.actor` | SaaS portal publish | Plesk webspace `sub.actor/httpdocs` (live); Docker Traefik path documented |
| `control.sub.actor` | Authenticated Control | Documented on Docker VPS; live Founder still via `founder.subactor.com` |
| `*.sub.actor` tenants | Tenant vhosts | Documented Traefik wildcard; live depends on portal stack |

`control.subactor.com` is **not** in the bindings registry.

---

## 2. Current procedures (canonical scripts)

### A. Static / PHP public pages (`*.subactor.com`, often `sub.actor`)

1. Exact deployment binding + plan hash (Digital Twin / Control).
2. `plesk://host/site/command/sync` (SFTP) — credentials from Vault scopes, not
in Git.
3. HTTP/hash read-back on the bound URL.
4. Entry points: Planfile tickets, `platform/scripts/deploy-public-pages.mjs`,
project reconciliation.

**redeploy role:** inventory in `examples/subactor/fleet.yaml`; verify steps in
`01-plesk-edge-verify` (curl EQL). Publish mutate stays Subactor `plesk://`
(authority + binding), not a free-form rsync from redeploy.

### B. Founder public Control (`founder.subactor.com`)

1. Publish `projekty/founder-subactor-com/plesk-origin/{app.js,package.json}` to
Plesk docroot `founder.subactor.com/`.
2. Keep `founder-plesk-tunnel.service` on Lenovo
(`platform/scripts/founder-plesk-tunnel.sh`) → `prototypowanie.pl:19081`.
3. Doctor: `platform/scripts/founder-public-origin-doctor.sh`.
4. EQL: `/` → 401 Basic; `/founder/form` → 200; `/__origin_health` → 200.

**redeploy role:** `02-founder-origin-verify` migration (health probes only).

### C. Platform Compose (lab / future VPS)

```bash
cd /opt/subactor/src/platform # or sibling checkout
./scripts/deploy-stack.sh deploy
```

Posture from host `.env` (`SUBACTOR_DEPLOYMENT_TOPOLOGY`,
`CONTROL_DEPLOYMENT_POSTURE`). Never `compose down -v` in deploy procedures.

### D. Documented full portal + Platform (Docker VPS target)

From `www-sub-actor/docs/deployment.md`:

1. Host fit per Policy DSL (16 GiB min / 32 GiB recommended).
2. `capture-platform-lock.sh` → `sync-platform.sh` (exact SHAs).
3. Secrets on host only; GHCR image `ghcr.io/subactor/www-sub-actor:sha-*`.
4. `deploy-all.sh docker <PORTAL_IMAGE>` → Platform then portal.
5. Health: `https://sub.actor/readyz`, tenant resolve smoke.
6. GitHub Environments `staging` / `production` + `release.sh` over SSH.

**redeploy role:** `03-docker-vps-platform` migration wraps the same SSH command
surface for detect/plan/apply and fleet targeting.

### E. auth.subactor.com

Static bootstrap only. Full SSO/OIDC federation is **not** the current runtime.
Do not treat `auth.subactor.com` as the production IdP until a binding + provider
say so.

---

## 3. Using `redeploy` for Subactor

Install: `pipx install redeploy` (see root README).

```bash
cd examples/subactor

# Fleet inventory (roles / tags — no secrets)
redeploy fleet --file fleet.yaml

# Plan only (no SSH)
redeploy run 03-docker-vps-platform/migration.yaml --plan-only

# Dry-run against a configured host (set SUBACTOR_VPS_SSH in env / edit host)
redeploy run 03-docker-vps-platform/migration.yaml --dry-run

# Verify-only edge probes (read-only)
redeploy run 01-plesk-edge-verify/migration.yaml --plan-only
redeploy run 02-founder-origin-verify/migration.yaml --plan-only
```

### Safety rules (fail closed)

| Rule | Why |
| --- | --- |
| No Compose stack inside Plesk | Policy `FORBID RUN_COMPOSE_STACK_INSIDE_PLESK` |
| No Control port 8181 on public host | Policy `FORBID BIND_CONTROL_PORT_8181_PUBLIC` |
| No mutable `main` multi-repo deploy | Use `components.lock` / `sha-*` images |
| No secrets in `migration.yaml` / Git | Host files + Vault; placeholders only here |
| `redeploy apply` ≠ production authority | Subactor grant / binding still required for mutate |
| Validator merge ≠ production apply | Separate POA / Planfile / SSH release |

---

## 4. Example package layout

```text
examples/subactor/
├── README.md
├── fleet.yaml # roles: plesk-edge, founder-origin, docker-vps, rpi5-edge
├── 01-plesk-edge-verify/
│ ├── migration.yaml # curl EQL for public pages
│ └── redeploy.yaml
├── 02-founder-origin-verify/
│ ├── migration.yaml # founder EQL + tunnel hint
│ └── redeploy.yaml
└── 03-docker-vps-platform/
├── migration.yaml # deploy-all.sh / release surface
└── redeploy.yaml
```

Hosts in examples use placeholders:

- `deploy@SUBACTOR_VPS_IP` — Docker production VPS
- `operator@LENOVO_LAB` — tunnel / Control lab host
- Plesk verify steps run from the operator machine (no Plesk root required)

Replace placeholders before `--dry-run` / apply. Prefer environment-specific
copies outside Git for real IPs.

---

## 5. Cross-links (do not duplicate SSOT)

| Concern | Canonical location |
| --- | --- |
| Host MUST/SHOULD tables | `wellmanifest/policy-dsl` + `www-sub-actor/docs/deployment.md` §1 |
| Portal/PayPal/ACME/GitHub release | `www-sub-actor/docs/deployment.md` |
| Domain → docroot bindings | `subactor/platform/config/deployment-bindings/registry.v1.json` |
| Founder origin ops | `subactor/projekty/founder-subactor-com/README.md` |
| Auth bootstrap | `subactor/projekty/auth-subactor-com/README.md` |
| redeploy CLI mechanics | `semcod/redeploy/README.md`, `docs/fleet.md` |

When this document disagrees with Subactor registries or live knowledge, **Subactor
SSOT wins**; update this file in the same change set.

---

## 6. Suggested operator sequence (target VPS cutover)

1. `redeploy run 01-plesk-edge-verify/... --plan-only` — baseline public EQL.
2. Qualify VPS with Policy DSL production-server profile.
3. Bootstrap Docker host (`www-sub-actor/scripts/host-bootstrap.sh`).
4. Pin sources (`capture-platform-lock` / `sync-platform`).
5. `redeploy run 03-docker-vps-platform/... --dry-run` then apply under grant.
6. Point `sub.actor` / `*.sub.actor` A/AAAA at VPS; keep Plesk for `*.subactor.com`
edge until intentionally migrated.
7. Move Founder public URL to `https://control.sub.actor/founder` only after
Control is healthy on Traefik; keep tunnel as rollback until EQL green.
8. Re-run verify migrations; backup Postgres / `acme-data` / secrets.
9 changes: 7 additions & 2 deletions examples/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -41,10 +41,15 @@ They are not useful as:

```text
examples/
├── yaml/ # supported, tested examples
└── md/ # three supported markdown subset examples plus prototypes
├── yaml/ # supported, tested examples
├── md/ # three supported markdown subset examples plus prototypes
└── subactor/ # Subactor Plesk edge + Docker VPS fleet (docs + verify wrappers)
```

Subactor operators: start at
[`subactor/README.md`](subactor/README.md) and
[`../docs/subactor-deployment.md`](../docs/subactor-deployment.md).

## Notes

- Several YAML examples use named library steps via `StepLibrary`.
Expand Down
58 changes: 58 additions & 0 deletions examples/subactor/01-plesk-edge-verify/migration.yaml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
# 01 — Plesk edge verify (read-only documentation surface)
# Usage: redeploy run examples/subactor/01-plesk-edge-verify/migration.yaml --plan-only
#
# Mutating Plesk publish stays in Subactor (plesk:// + deployment bindings).
# Hosts below are placeholders; verify steps are local curl from the planner.

name: "subactor plesk-edge verify (public pages)"
description: >-
Read-only EQL probes for live Plesk-hosted Subactor surfaces.
Does not sync docroots.

source:
strategy: docker_full
host: "operator@localhost"
app: subactor-edge
version: "live"
domain: subactor.com
remote_dir: /tmp/subactor-redeploy-noop

target:
strategy: docker_full
host: "operator@localhost"
app: subactor-edge
version: "live"
domain: subactor.com
remote_dir: /tmp/subactor-redeploy-noop
verify_url: https://subactor.com/
verify_version: "live"

extra_steps:
- id: eql_subactor_com
action: ssh_cmd
description: "EQL marketing root responds"
command: >-
curl -fsS -o /dev/null -w '%{http_code}\n' --max-time 20 https://subactor.com/
| grep -E '^(200|301|302)$'
risk: low

- id: eql_auth_bootstrap
action: ssh_cmd
description: "auth.subactor.com bootstrap is reachable (static, not full IdP)"
command: >-
curl -fsS -o /dev/null -w '%{http_code}\n' --max-time 20 https://auth.subactor.com/
| grep -E '^(200|301|302)$'
risk: low

- id: eql_sub_actor
action: ssh_cmd
description: "sub.actor responds (live path may still be Plesk httpdocs)"
command: >-
curl -fsS -o /dev/null -w '%{http_code}\n' --max-time 20 https://sub.actor/
| grep -E '^(200|301|302|401|403)$'
risk: low

notes:
- "SSOT bindings: subactor/platform/config/deployment-bindings/registry.v1.json"
- "auth.subactor.com is informational bootstrap — not production OIDC yet"
- "Prefer --plan-only until hosts are intentionally set for dry-run"
5 changes: 5 additions & 0 deletions examples/subactor/01-plesk-edge-verify/redeploy.yaml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
spec: migration.yaml
host: operator@localhost
app: subactor-edge
domain: subactor.com
remote_dir: /tmp/subactor-redeploy-noop
67 changes: 67 additions & 0 deletions examples/subactor/02-founder-origin-verify/migration.yaml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
# 02 — Founder public origin verify
# Usage: redeploy run examples/subactor/02-founder-origin-verify/migration.yaml --plan-only
#
# Architecture (live 2026-08-15):
# Internet → founder.subactor.com (Plesk TLS + Node)
# → 127.0.0.1:19081 ← SSH -R from Lenovo
# → Caddy :18081 → hr-control

name: "subactor founder.subactor.com origin verify"
description: >-
Read-only EQL for Founder public origin. Does not restart the tunnel unit.

source:
strategy: docker_full
host: "operator@LENOVO_LAB"
app: founder-origin
version: "live"
domain: founder.subactor.com
remote_dir: /tmp/subactor-redeploy-noop

target:
strategy: docker_full
host: "operator@LENOVO_LAB"
app: founder-origin
version: "live"
domain: founder.subactor.com
remote_dir: /tmp/subactor-redeploy-noop
verify_url: https://founder.subactor.com/__origin_health
verify_version: "live"

extra_steps:
- id: eql_founder_root_unauthorized
action: ssh_cmd
description: "GET / without credentials must be 401 Basic"
command: >-
code=$(curl -sS -o /dev/null -w '%{http_code}' --max-time 20 https://founder.subactor.com/);
test "$code" = "401" && echo founder-root-401-ok
risk: low

- id: eql_founder_form
action: ssh_cmd
description: "GET /founder/form returns Founder HTML"
command: >-
curl -fsS --max-time 20 https://founder.subactor.com/founder/form
| grep -q 'Subactor' && echo founder-form-ok
risk: low

- id: eql_origin_health
action: ssh_cmd
description: "Origin health JSON endpoint"
command: >-
curl -fsS --max-time 20 https://founder.subactor.com/__origin_health
| grep -qi . && echo origin-health-ok
risk: low

- id: hint_tunnel_unit
action: ssh_cmd
description: "Remind operator to check user systemd tunnel on Lenovo"
command: >-
echo "Check: systemctl --user status founder-plesk-tunnel.service";
echo "Script: platform/scripts/founder-plesk-tunnel.sh"
risk: low

notes:
- "Project docs: subactor/projekty/founder-subactor-com/README.md"
- "Without tunnel, proxied paths return 502"
- "Target cutover: https://control.sub.actor/founder after Docker VPS is live"
5 changes: 5 additions & 0 deletions examples/subactor/02-founder-origin-verify/redeploy.yaml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
spec: migration.yaml
host: operator@LENOVO_LAB
app: founder-origin
domain: founder.subactor.com
remote_dir: /tmp/subactor-redeploy-noop
Loading
Loading