|
| 1 | +# Project Wharf — Local Deployment Session Summary |
| 2 | +<!-- SPDX-License-Identifier: PMPL-1.0-or-later --> |
| 3 | + |
| 4 | +**Date:** 2026-02-14 |
| 5 | +**Status:** COMMITTED TO GITHUB. GitLab push pending (broken pack object). |
| 6 | + |
| 7 | +--- |
| 8 | + |
| 9 | +## What Was Done |
| 10 | + |
| 11 | +### Bug Fixes (3 files) |
| 12 | + |
| 13 | +| File | Bug | Fix | |
| 14 | +|------|-----|-----| |
| 15 | +| `infra/containers/Containerfile.agent` | Wolfi doesn't have `musl-dev`, `cargo`, or `rustup` packages | Switched to glibc build: `rust pkgconf openssl-dev gcc glibc-dev`, removed musl target/RUSTFLAGS, build from `target/release/`, runtime `wolfi-base` | |
| 16 | +| `Containerfile` (line 35) | yacht-agent stage uses `static` image but binary is glibc-linked → segfault | Changed to `cgr.dev/chainguard/wolfi-base:latest` | |
| 17 | +| `adapters/wordpress-wharf/wharf-adapter.php` | Reads flat `$stats['queries_allowed']` but `/stats` returns nested `{"queries":{"allowed":N}}`. Also reads `moored`/`firewall_mode` from `/stats` but they're on `/status` | Rewrote `wharf_fetch_agent_stats()` to fetch both `/stats` and `/status`, maps nested JSON to flat format | |
| 18 | + |
| 19 | +### New Files (4 files) |
| 20 | + |
| 21 | +| File | Purpose | |
| 22 | +|------|---------| |
| 23 | +| `deploy/Containerfile.ols-local` | OLS 1.8.5 + lsphp83 with mysql/curl/intl/imagick/redis extensions | |
| 24 | +| `deploy/compose.yaml` | 3-service podman compose: db (MariaDB 10.11), agent (yacht-agent), web (OLS) | |
| 25 | +| `deploy/local-test.sh` | Automated: create dirs, download WP, fetch real salt keys, write wp-config.php (DB_HOST=agent:3306), copy adapter plugin, build containers, start stack, health check | |
| 26 | +| `deploy/verify-sqli.sh` | 6 SQL injection tests via `podman exec` through agent proxy | |
| 27 | + |
| 28 | +### Documentation Updated (8 files) |
| 29 | + |
| 30 | +| File | Changes | |
| 31 | +|------|---------| |
| 32 | +| `CHANGELOG.adoc` | Added "Local Deployment Proof" section | |
| 33 | +| `TOPOLOGY.md` | Added local deployment architecture diagram + `local-deploy` row in dashboard | |
| 34 | +| `AI.a2ml` | Added "Local Deployment" section | |
| 35 | +| `deploy/DEPLOY.adoc` | Added local dev instructions, architecture diagram, services table | |
| 36 | +| `.machine_readable/STATE.scm` | Added `local-deployment-proof` milestone with 5 sub-milestones | |
| 37 | +| `.machine_readable/ECOSYSTEM.scm` | Added `openlitespeed` and `mariadb` as related projects | |
| 38 | +| `.machine_readable/META.scm` | Added 2 ADRs: `glibc-runtime-containers`, `ols-local-deployment` | |
| 39 | +| `.gitignore` | Added `deploy/wharf-local/` | |
| 40 | + |
| 41 | +### Verification Results |
| 42 | + |
| 43 | +- **cargo test**: 63/63 passed (40 wharf-core + 19 wharf-cli + 4 yacht-agent) |
| 44 | +- **Stack**: All 3 containers healthy, WordPress installation wizard served at :8080 |
| 45 | +- **Agent**: `/health` → OK, `/stats` → real counters (321 allowed, 3 blocked), `/status` → active |
| 46 | +- **verify-sqli.sh**: 6/6 passed |
| 47 | + - SELECT → PASS (forwarded) |
| 48 | + - INSERT INTO wp_users → BLOCKED |
| 49 | + - DROP TABLE → BLOCKED |
| 50 | + - ALTER TABLE → BLOCKED |
| 51 | + - TRUNCATE TABLE → BLOCKED |
| 52 | + - UNION SELECT injection → BLOCKED |
| 53 | + |
| 54 | +--- |
| 55 | + |
| 56 | +## Commit |
| 57 | + |
| 58 | +``` |
| 59 | +ece13ac feat: local E2E deployment proof — WordPress behind yacht-agent SQL proxy |
| 60 | +``` |
| 61 | + |
| 62 | +15 files changed, 578 insertions(+), 38 deletions(-) |
| 63 | + |
| 64 | +**Pushed to:** GitHub ✅ |
| 65 | +**Pushed to:** GitLab ❌ (see below) |
| 66 | + |
| 67 | +--- |
| 68 | + |
| 69 | +## Remaining: GitLab Push |
| 70 | + |
| 71 | +**Two issues blocking GitLab push:** |
| 72 | + |
| 73 | +### Issue 1: Diverged history |
| 74 | +GitLab has 3 old MR-based commits not in GitHub's history (`64fc965`, `5827c3f`, `bf1aac3`). Needs force-push to overwrite with canonical GitHub history. |
| 75 | + |
| 76 | +### Issue 2: `main` branch is protected + API token expired |
| 77 | +- GitLab `main` branch is protected (no force push allowed) |
| 78 | +- GitLab API token (`glpat-dYz2_j--...`) is **EXPIRED** |
| 79 | +- SSH auth works but can't change branch protection via SSH |
| 80 | +- `glab` CLI also uses the expired token |
| 81 | + |
| 82 | +### Fix (manual steps required) |
| 83 | + |
| 84 | +```bash |
| 85 | +# Step 1: Refresh GitLab token |
| 86 | +# Go to: https://gitlab.com/-/user_settings/personal_access_tokens |
| 87 | +# Create new token with api + write_repository scopes |
| 88 | +# Update ~/.netrc with new token |
| 89 | + |
| 90 | +# Step 2: Unprotect main |
| 91 | +GL_TOKEN="<new-token>" |
| 92 | +PROJECT_ID=$(curl -sf --header "PRIVATE-TOKEN: $GL_TOKEN" \ |
| 93 | + "https://gitlab.com/api/v4/projects?search=project-wharf&owned=true" | python3 -c "import sys,json; print(json.load(sys.stdin)[0]['id'])") |
| 94 | +curl --request DELETE --header "PRIVATE-TOKEN: $GL_TOKEN" \ |
| 95 | + "https://gitlab.com/api/v4/projects/$PROJECT_ID/protected_branches/main" |
| 96 | + |
| 97 | +# Step 3: Force push from clean clone (already at /tmp/wharf-push.git) |
| 98 | +cd /tmp/wharf-push.git |
| 99 | +git push --force git@gitlab.com:hyperpolymath/project-wharf.git main |
| 100 | + |
| 101 | +# Step 4: Re-protect main |
| 102 | +curl --request POST --header "PRIVATE-TOKEN: $GL_TOKEN" \ |
| 103 | + "https://gitlab.com/api/v4/projects/$PROJECT_ID/protected_branches?name=main&push_access_level=40&merge_access_level=40" |
| 104 | + |
| 105 | +# Step 5: Clean up |
| 106 | +rm -rf /tmp/wharf-push.git |
| 107 | +``` |
| 108 | + |
| 109 | +### Also fix the local repo's broken pack (optional) |
| 110 | + |
| 111 | +The local `.git` has a broken pack object (`501faf18`) from the old GitLab fetch. Not urgent — local repo works fine for everything except `git gc`. |
| 112 | + |
| 113 | +```bash |
| 114 | +cd /var$REPOS_DIR/project-wharf |
| 115 | +git remote remove gitlab |
| 116 | +git remote add gitlab git@gitlab.com:hyperpolymath/project-wharf.git |
| 117 | +# After GitLab is force-pushed with clean history: |
| 118 | +git fetch gitlab |
| 119 | +``` |
| 120 | + |
| 121 | +--- |
| 122 | + |
| 123 | +## How to Use the Local Stack |
| 124 | + |
| 125 | +```bash |
| 126 | +cd /var$REPOS_DIR/project-wharf/deploy |
| 127 | + |
| 128 | +# Start everything (downloads WP, builds containers, starts stack) |
| 129 | +bash local-test.sh |
| 130 | + |
| 131 | +# WordPress setup wizard |
| 132 | +# http://localhost:8080 |
| 133 | + |
| 134 | +# Agent API |
| 135 | +curl http://localhost:9001/health |
| 136 | +curl http://localhost:9001/stats |
| 137 | + |
| 138 | +# Prove SQL injection blocked |
| 139 | +bash verify-sqli.sh |
| 140 | + |
| 141 | +# Tear down |
| 142 | +podman compose down |
| 143 | +rm -rf wharf-local/ # removes all runtime data |
| 144 | +``` |
| 145 | + |
| 146 | +### Key Architecture Detail |
| 147 | + |
| 148 | +``` |
| 149 | +Browser :8080 → OLS (port 80) → PHP/WordPress → agent:3306 (yacht-agent) → db:3306 (MariaDB) |
| 150 | + ↑ |
| 151 | + AST SQL parser (sqlparser 0.39) |
| 152 | + Blocks: INSERT wp_users, DROP, ALTER, TRUNCATE, UNION |
| 153 | +``` |
| 154 | + |
| 155 | +WordPress `wp-config.php` has `DB_HOST = 'agent:3306'` — all SQL goes through the proxy. |
| 156 | +MariaDB is NOT exposed to the host network. |
| 157 | + |
| 158 | +--- |
| 159 | + |
| 160 | +## Next Steps (Future Sessions) |
| 161 | + |
| 162 | +1. **GitLab push** — use the fix above |
| 163 | +2. **Production VPS** — deploy to stamp-protocol.org with real TLS |
| 164 | +3. **WordPress setup** — complete the install wizard, activate Wharf adapter plugin |
| 165 | +4. **Bitbucket/Codeberg** — mirror when those forges are unblocked |
0 commit comments