Skip to content

Commit ece13ac

Browse files
Jonathan D.A. Jewellclaude
andcommitted
feat: local E2E deployment proof — WordPress behind yacht-agent SQL proxy
Add podman compose stack (MariaDB, yacht-agent, OpenLiteSpeed) with automated setup and SQL injection verification (6/6 attacks blocked). Fix Containerfile.agent Wolfi packages (musl→glibc), main Containerfile yacht-agent runtime (static→wolfi-base), and WordPress adapter stats field mismatch (now fetches both /stats and /status endpoints). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent c991ce7 commit ece13ac

15 files changed

Lines changed: 578 additions & 38 deletions

File tree

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ htmlcov/
7878
*.tmp
7979
*.bak
8080

81+
# Local deployment runtime data
82+
deploy/wharf-local/
83+
8184
# AI/Sync artifacts
8285
ai-cli-crash-capture/
8386
.directory

.machine_readable/ECOSYSTEM.scm

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,6 @@
1616
(project "hypatia" (relationship ci-cd-tool) (description "Neurosymbolic CI/CD security intelligence"))
1717
(project "gitbot-fleet" (relationship automation) (description "Bot orchestration: rhodibot, echidnabot, sustainabot, glambot, seambot, finishbot"))
1818
(project "verisimdb-data" (relationship quality-data) (description "Security scan data ingestion for weak point tracking"))
19-
(project "wordpress" (relationship adapter-target) (description "Primary CMS adapter — GPL-2.0 dashboard plugin in adapters/wordpress-wharf/"))))
19+
(project "wordpress" (relationship adapter-target) (description "Primary CMS adapter — GPL-2.0 dashboard plugin in adapters/wordpress-wharf/"))
20+
(project "openlitespeed" (relationship web-server) (description "High-performance web server for local and production deployment (litespeedtech/openlitespeed:1.8.5-lsphp83)"))
21+
(project "mariadb" (relationship database-backend) (description "MariaDB 10.11 hidden behind yacht-agent SQL proxy"))))

.machine_readable/META.scm

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,12 @@
2929
(adr "ebpf-xdp-firewall"
3030
(status accepted)
3131
(description "Kernel-level packet filtering via eBPF XDP with nftables fallback.")
32-
(rationale "XDP processes packets before the kernel network stack for lowest latency. Cascade to nftables ensures protection without CAP_BPF."))))
32+
(rationale "XDP processes packets before the kernel network stack for lowest latency. Cascade to nftables ensures protection without CAP_BPF."))
33+
(adr "glibc-runtime-containers"
34+
(status accepted)
35+
(description "Yacht-agent containers use wolfi-base runtime (glibc) instead of static/distroless.")
36+
(rationale "Wolfi Rust toolchain produces glibc-linked binaries. Static image has no glibc, causing runtime segfaults. Wolfi-base provides glibc with minimal attack surface."))
37+
(adr "ols-local-deployment"
38+
(status accepted)
39+
(description "Local deployment uses OpenLiteSpeed with WordPress behind yacht-agent SQL proxy.")
40+
(rationale "OLS provides native PHP LSAPI (faster than FastCGI), WordPress connects to agent:3306 which parses SQL AST before forwarding to MariaDB."))))

.machine_readable/STATE.scm

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,13 @@
2626
(sub-milestone "hack-me-challenge" (status complete) (description "HACK-ME-CHALLENGE.adoc with rules of engagement and technical details"))
2727
(sub-milestone "wordpress-adapter" (status complete) (description "GPL-2.0 PHP plugin for wp-admin dashboard widget + admin bar indicator"))
2828
(sub-milestone "systemd-packaging" (status complete) (description "yacht-agent.service with full systemd hardening"))
29-
(sub-milestone "tooling-enrollment" (status complete) (description "Bot directives, panic-attacker workflow, AI manifest updated"))))
29+
(sub-milestone "tooling-enrollment" (status complete) (description "Bot directives, panic-attacker workflow, AI manifest updated")))
30+
(milestone "local-deployment-proof" (status complete) (description "End-to-end local deployment with podman compose")
31+
(sub-milestone "containerfile-fixes" (status complete) (description "Fixed Wolfi packages in Containerfile.agent (musl→glibc), yacht-agent runtime stage (static→wolfi-base)"))
32+
(sub-milestone "wordpress-adapter-fix" (status complete) (description "Fixed stats/status field mismatch — now fetches both /stats and /status endpoints"))
33+
(sub-milestone "compose-stack" (status complete) (description "3-service compose: MariaDB, yacht-agent proxy, OpenLiteSpeed+WordPress"))
34+
(sub-milestone "automated-setup" (status complete) (description "local-test.sh: WP download, real salt keys, wp-config.php, adapter plugin, build+start"))
35+
(sub-milestone "sqli-verification" (status complete) (description "verify-sqli.sh: 6/6 attacks blocked (SELECT passes, INSERT/DROP/ALTER/TRUNCATE/UNION blocked)"))))
3036
(components
3137
(component "wharf-core" (path "crates/wharf-core") (role "shared library: crypto, fleet, integrity, mooring, sync"))
3238
(component "wharf-cli" (path "bin/wharf-cli") (role "offline admin CLI: moor, integrity, fleet management"))

AI.a2ml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,14 @@
2424
- AST-aware SQL proxy blocks injection at wire level
2525
- Fail-closed: if it can't verify, it blocks
2626

27+
## Local Deployment
28+
29+
- **compose stack**: `deploy/compose.yaml` — 3-service podman compose (db, agent, web)
30+
- **setup script**: `deploy/local-test.sh` — automated WordPress + stack setup
31+
- **SQLi proof**: `deploy/verify-sqli.sh` — 6/6 injection attacks provably blocked
32+
- **Architecture**: Browser→OLS→WordPress→agent:3306 (AST proxy)→MariaDB
33+
- WordPress `DB_HOST=agent:3306` — all SQL goes through yacht-agent
34+
2735
## Integration
2836

2937
- **panic-attack**: Weekly security scan via `.github/workflows/security-scan.yml`

CHANGELOG.adoc

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1919
- Comprehensive Justfile with 50+ recipes
2020
- RSR (Rhodium Standard Repository) compliance documentation
2121

22+
=== Local Deployment Proof (2026-02-14)
23+
- *Local compose stack*: `deploy/compose.yaml` with 3-service podman compose (MariaDB, yacht-agent proxy, OpenLiteSpeed+WordPress). Full end-to-end proof.
24+
- *Containerfile bug fixes*: `Containerfile.agent` fixed Wolfi package names (musl→glibc), main `Containerfile` fixed yacht-agent runtime stage (static→wolfi-base to avoid glibc segfault)
25+
- *WordPress adapter fix*: `wharf-adapter.php` now fetches both `/stats` and `/status` endpoints, mapping nested JSON to flat format the widget expects
26+
- *OLS container*: `deploy/Containerfile.ols-local` wrapping litespeedtech/openlitespeed:1.8.5-lsphp83 with WordPress-required PHP extensions
27+
- *Automated setup*: `deploy/local-test.sh` downloads WordPress, fetches real salt keys, generates wp-config.php with `DB_HOST=agent:3306`, copies adapter plugin, builds and starts stack
28+
- *SQL injection verification*: `deploy/verify-sqli.sh` proves 6/6 attacks blocked (SELECT passes, INSERT wp_users/DROP/ALTER/TRUNCATE/UNION all blocked), with live agent stats
29+
2230
=== V1.0 Shipping Preparation (2026-02-14)
2331
- *Signature scheme feature flag*: Ed448 now opt-in via `SignatureScheme::Hybrid`; default is `MlDsa87Only` (FIPS 204 peer-reviewed). Configurable via `--signature-scheme` CLI arg and TOML config.
2432
- *SQL injection smoke tests*: 19 integration tests proving AST-based proxy blocks real WordPress attack patterns (user creation, options manipulation, content defacement, DDL, stacked injection)
@@ -71,7 +79,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7179

7280
| Version | Date | Highlights |
7381
|---------|------|------------|
74-
| Unreleased | 2026-02-14 | V1.0 shipping: signature scheme flag, SQLi tests, deployment package, WordPress adapter, hack-me challenge |
82+
| Unreleased | 2026-02-14 | Local E2E deployment proof, Containerfile fixes, WordPress adapter stats fix, compose stack, SQLi verification |
7583
| 0.1.0 | 2025-11-26 | Initial release |
7684

7785
== Upgrade Guide

Containerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ COPY --from=builder /build/target/release/wharf /usr/local/bin/wharf
3131
ENTRYPOINT ["wharf"]
3232
CMD ["--help"]
3333

34-
# Runtime image for yacht-agent (distrolesszero attack surface)
35-
FROM cgr.dev/chainguard/static:latest AS yacht-agent
34+
# Runtime image for yacht-agent (wolfi-baseglibc required)
35+
FROM cgr.dev/chainguard/wolfi-base:latest AS yacht-agent
3636

3737
COPY --from=builder /build/target/release/yacht-agent /yacht-agent
3838

TOPOLOGY.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,21 @@
5151
| wharf-ebpf (optional, excluded from default build) |
5252
| XDP kernel program + userspace loader (aya 0.12) |
5353
+-----------------------------------------------------------+
54+
55+
LOCAL DEPLOYMENT (deploy/compose.yaml)
56+
+-----------------------------------------------------------+
57+
| |
58+
| Browser :8080 → OLS (web) → PHP/WordPress |
59+
| ↓ |
60+
| agent:3306 (yacht-agent proxy) |
61+
| ↓ ↑ |
62+
| AST SQL parser /stats /status /health |
63+
| (sqlparser 0.39) (API :9001) |
64+
| ↓ |
65+
| db:3306 (MariaDB 10.11) |
66+
| |
67+
| Blocked: INSERT wp_users, DROP, ALTER, TRUNCATE, UNION |
68+
+-----------------------------------------------------------+
5469
```
5570

5671
## Completion Dashboard
@@ -79,8 +94,9 @@
7994
| - signature scheme | `[██████████]` 100% | CLI flag --signature-scheme, config support |
8095
| **wharf-ebpf** | `[████████░░]` 80% | XDP program + loader, needs production testing |
8196
| **nebula.rs** | `[██████████]` 100% | CA, cert signing, IP allocation, revocation |
82-
| **wordpress-adapter** | `[██████████]` 100% | GPL-2.0 dashboard widget + admin bar indicator |
97+
| **wordpress-adapter** | `[██████████]` 100% | GPL-2.0 dashboard widget + admin bar indicator (stats/status fixed) |
8398
| **deployment** | `[██████████]` 100% | setup.sh, DEPLOY.adoc, systemd unit, selur-compose |
99+
| **local-deploy** | `[██████████]` 100% | compose.yaml, local-test.sh, verify-sqli.sh — E2E proven |
84100
| **Overall** | `[█████████░]` 95% | ed448 audit pending, otherwise shipping |
85101

86102
## Key Dependencies

adapters/wordpress-wharf/wharf-adapter.php

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,13 @@ function wharf_add_dashboard_widget() {
3737
add_action('wp_dashboard_setup', 'wharf_add_dashboard_widget');
3838

3939
/**
40-
* Fetch stats from yacht-agent API
40+
* Fetch stats and status from yacht-agent API
41+
*
42+
* Merges /stats (query counters) and /status (mooring, components) into a
43+
* flat array the dashboard widget expects.
44+
*
45+
* /stats returns: {"queries":{"allowed":N,"blocked":N,"audited":N},"mooring_sessions":N,"integrity_checks":N}
46+
* /status returns: {"status":"active","moored":bool,"version":"...","components":{...}}
4147
*/
4248
function wharf_fetch_agent_stats() {
4349
$cache_key = 'wharf_agent_stats';
@@ -46,24 +52,41 @@ function wharf_fetch_agent_stats() {
4652
return $cached;
4753
}
4854

49-
$response = wp_remote_get(WHARF_AGENT_URL . '/stats', array(
50-
'timeout' => 3,
51-
'sslverify' => false,
52-
));
55+
$request_args = array('timeout' => 3, 'sslverify' => false);
5356

54-
if (is_wp_error($response)) {
55-
return array('error' => $response->get_error_message());
57+
// Fetch /stats
58+
$stats_response = wp_remote_get(WHARF_AGENT_URL . '/stats', $request_args);
59+
if (is_wp_error($stats_response)) {
60+
return array('error' => $stats_response->get_error_message());
61+
}
62+
$stats_data = json_decode(wp_remote_retrieve_body($stats_response), true);
63+
if (!is_array($stats_data)) {
64+
return array('error' => 'Invalid response from yacht-agent /stats');
5665
}
5766

58-
$body = wp_remote_retrieve_body($response);
59-
$data = json_decode($body, true);
60-
61-
if (!is_array($data)) {
62-
return array('error' => 'Invalid response from yacht-agent');
67+
// Fetch /status
68+
$status_response = wp_remote_get(WHARF_AGENT_URL . '/status', $request_args);
69+
if (is_wp_error($status_response)) {
70+
return array('error' => $status_response->get_error_message());
71+
}
72+
$status_data = json_decode(wp_remote_retrieve_body($status_response), true);
73+
if (!is_array($status_data)) {
74+
return array('error' => 'Invalid response from yacht-agent /status');
6375
}
6476

65-
set_transient($cache_key, $data, 30); // Cache for 30 seconds
66-
return $data;
77+
// Map nested responses to flat format for the widget
78+
$merged = array(
79+
'queries_allowed' => isset($stats_data['queries']['allowed']) ? $stats_data['queries']['allowed'] : 0,
80+
'queries_blocked' => isset($stats_data['queries']['blocked']) ? $stats_data['queries']['blocked'] : 0,
81+
'queries_audited' => isset($stats_data['queries']['audited']) ? $stats_data['queries']['audited'] : 0,
82+
'moored' => !empty($status_data['moored']),
83+
'firewall_mode' => isset($status_data['components']['shield']) ? $status_data['components']['shield'] : 'unknown',
84+
'signature_scheme' => 'ed25519',
85+
'version' => isset($status_data['version']) ? $status_data['version'] : 'unknown',
86+
);
87+
88+
set_transient($cache_key, $merged, 30);
89+
return $merged;
6790
}
6891

6992
/**

deploy/Containerfile.ols-local

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# SPDX-License-Identifier: PMPL-1.0-or-later
2+
# SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell (hyperpolymath) <jonathan.jewell@open.ac.uk>
3+
#
4+
# OpenLiteSpeed local dev image — WordPress-ready with MySQL extensions
5+
# EXCEPTION: No Chainguard OLS image exists — litespeedtech base required
6+
7+
FROM docker.io/litespeedtech/openlitespeed:1.8.5-lsphp83
8+
9+
# lsphp83-common (bundled in base) provides gd, mbstring, xml, zip
10+
# Only need to add packages not already included
11+
RUN apt-get update && apt-get install -y --no-install-recommends \
12+
lsphp83-mysql \
13+
lsphp83-curl \
14+
lsphp83-intl \
15+
lsphp83-imagick \
16+
lsphp83-redis \
17+
&& apt-get clean \
18+
&& rm -rf /var/lib/apt/lists/*
19+
20+
EXPOSE 8088
21+
22+
WORKDIR /var/www/vhosts/localhost/html

0 commit comments

Comments
 (0)