Skip to content

Commit 01431b5

Browse files
committed
feat(adapters): add WordPress integration adapter
The WP edition's reason for being. PHP adapter that bridges WordPress to project-wharf's deployment + integrity layer. - adapters/wordpress-wharf/wharf-adapter.php - adapters/wordpress-wharf/readme.txt Per the 2026-04-26 graduation plan (AI-WORK-todo.md item -4).
1 parent b9e788e commit 01431b5

2 files changed

Lines changed: 222 additions & 0 deletions

File tree

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
=== Wharf Security Adapter ===
2+
Contributors: hyperpolymath
3+
Tags: security, firewall, database-proxy, post-quantum
4+
Requires at least: 6.0
5+
Tested up to: 6.7
6+
Stable tag: 1.0.0
7+
Requires PHP: 8.0
8+
License: GPLv2 or later
9+
License URI: https://www.gnu.org/licenses/gpl-2.0.html
10+
11+
Dashboard widget and status display for sites protected by Project Wharf.
12+
13+
== Description ==
14+
15+
Project Wharf is a Sovereign Web Hypervisor — an external security layer that sits
16+
between the internet and your WordPress site. This adapter plugin provides a
17+
dashboard widget showing the protection status.
18+
19+
**This plugin does NO security work itself.** All security enforcement is handled by
20+
the yacht-agent, a separate Rust daemon that:
21+
22+
* Proxies all database queries through an AST-aware SQL parser
23+
* Blocks SQL injection at the wire protocol level (not regex)
24+
* Monitors file integrity via BLAKE3 cryptographic hashes
25+
* Enforces write policies (immutable tables, no DROP/ALTER)
26+
* Uses ML-DSA-87 post-quantum signatures for the control plane
27+
28+
= What This Plugin Does =
29+
30+
* Adds a "Wharf Security Status" widget to your WordPress dashboard
31+
* Shows query statistics (total, blocked, audited)
32+
* Displays firewall mode and signature scheme
33+
* Adds an admin bar indicator (green = protected, grey = agent unreachable)
34+
* Caches agent responses for 30 seconds to minimise overhead
35+
36+
= What This Plugin Does NOT Do =
37+
38+
* Does not implement any security features (that's the yacht-agent's job)
39+
* Does not modify your database or files
40+
* Does not phone home or collect telemetry
41+
* Does not require an account or subscription
42+
43+
== Installation ==
44+
45+
1. Install and configure Project Wharf on your server (see https://github.com/hyperpolymath/project-wharf)
46+
2. Upload the `wharf-adapter` folder to `/wp-content/plugins/`
47+
3. Activate the plugin through the 'Plugins' menu
48+
4. Visit your Dashboard to see the Wharf Security Status widget
49+
50+
= Configuration =
51+
52+
Set the `WHARF_AGENT_URL` environment variable if your yacht-agent runs on a
53+
non-default address:
54+
55+
define('WHARF_AGENT_URL', 'http://agent:9001');
56+
57+
Default is `http://localhost:9001`.
58+
59+
== Changelog ==
60+
61+
= 1.0.0 =
62+
* Initial release
63+
* Dashboard widget with query stats
64+
* Admin bar status indicator
65+
* Agent health monitoring
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
<?php
2+
/**
3+
* Plugin Name: Wharf Security Adapter
4+
* Plugin URI: https://github.com/hyperpolymath/project-wharf
5+
* Description: Dashboard widget and status display for sites protected by Project Wharf (yacht-agent).
6+
* Version: 1.0.0
7+
* Author: Jonathan D.A. Jewell
8+
* Author URI: https://github.com/hyperpolymath
9+
* License: GPL-2.0-or-later
10+
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
11+
* Text Domain: wharf-adapter
12+
*
13+
* SPDX-License-Identifier: GPL-2.0-or-later
14+
*
15+
* This plugin is a thin adapter — it does NO security work itself.
16+
* All security enforcement is handled by the yacht-agent (a separate Rust daemon).
17+
* This plugin simply reads status from the agent's API and displays it in wp-admin.
18+
*/
19+
20+
if (!defined('ABSPATH')) {
21+
exit;
22+
}
23+
24+
define('WHARF_ADAPTER_VERSION', '1.0.0');
25+
define('WHARF_AGENT_URL', getenv('WHARF_AGENT_URL') ?: 'http://localhost:9001');
26+
27+
/**
28+
* Register the dashboard widget
29+
*/
30+
function wharf_add_dashboard_widget() {
31+
wp_add_dashboard_widget(
32+
'wharf_security_status',
33+
'Wharf Security Status',
34+
'wharf_render_dashboard_widget'
35+
);
36+
}
37+
add_action('wp_dashboard_setup', 'wharf_add_dashboard_widget');
38+
39+
/**
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":{...}}
47+
*/
48+
function wharf_fetch_agent_stats() {
49+
$cache_key = 'wharf_agent_stats';
50+
$cached = get_transient($cache_key);
51+
if ($cached !== false) {
52+
return $cached;
53+
}
54+
55+
$request_args = array('timeout' => 3, 'sslverify' => false);
56+
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');
65+
}
66+
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');
75+
}
76+
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;
90+
}
91+
92+
/**
93+
* Render the dashboard widget
94+
*/
95+
function wharf_render_dashboard_widget() {
96+
$stats = wharf_fetch_agent_stats();
97+
98+
if (isset($stats['error'])) {
99+
echo '<p style="color:#d63638"><strong>Agent Unreachable:</strong> ';
100+
echo esc_html($stats['error']);
101+
echo '</p>';
102+
echo '<p>Ensure yacht-agent is running at <code>' . esc_html(WHARF_AGENT_URL) . '</code></p>';
103+
return;
104+
}
105+
106+
$queries_allowed = isset($stats['queries_allowed']) ? intval($stats['queries_allowed']) : 0;
107+
$queries_blocked = isset($stats['queries_blocked']) ? intval($stats['queries_blocked']) : 0;
108+
$moored = !empty($stats['moored']);
109+
$firewall = isset($stats['firewall_mode']) ? esc_html($stats['firewall_mode']) : 'unknown';
110+
$sig_scheme = isset($stats['signature_scheme']) ? esc_html($stats['signature_scheme']) : 'unknown';
111+
112+
echo '<table class="widefat" style="border:0">';
113+
114+
// Status indicator
115+
$status_color = $moored ? '#00a32a' : '#dba617';
116+
$status_label = $moored ? 'Moored (Connected)' : 'Unmoored (Standalone)';
117+
echo '<tr><td><strong>Status</strong></td>';
118+
echo '<td><span style="color:' . $status_color . '">&#9679;</span> ' . $status_label . '</td></tr>';
119+
120+
// Query stats
121+
$total = $queries_allowed + $queries_blocked;
122+
echo '<tr><td><strong>DB Queries</strong></td>';
123+
echo '<td>' . number_format($total) . ' total (' . number_format($queries_blocked) . ' blocked)</td></tr>';
124+
125+
// Firewall
126+
echo '<tr><td><strong>Firewall</strong></td>';
127+
echo '<td>' . $firewall . '</td></tr>';
128+
129+
// Signature scheme
130+
echo '<tr><td><strong>Signatures</strong></td>';
131+
echo '<td>' . $sig_scheme . '</td></tr>';
132+
133+
echo '</table>';
134+
135+
echo '<p style="margin-top:12px;color:#757575;font-size:12px">';
136+
echo 'Protected by <a href="https://github.com/hyperpolymath/project-wharf" target="_blank">Project Wharf</a>';
137+
echo ' &mdash; The Sovereign Web Hypervisor';
138+
echo '</p>';
139+
}
140+
141+
/**
142+
* Add admin bar indicator
143+
*/
144+
function wharf_admin_bar_item($admin_bar) {
145+
$stats = wharf_fetch_agent_stats();
146+
$is_healthy = !isset($stats['error']);
147+
148+
$admin_bar->add_node(array(
149+
'id' => 'wharf-status',
150+
'title' => ($is_healthy ? '&#9679; ' : '&#9675; ') . 'Wharf',
151+
'href' => admin_url(),
152+
'meta' => array(
153+
'title' => $is_healthy ? 'Wharf: Protected' : 'Wharf: Agent unreachable',
154+
),
155+
));
156+
}
157+
add_action('admin_bar_menu', 'wharf_admin_bar_item', 100);

0 commit comments

Comments
 (0)