# BW Update Server

Custom WordPress plugin that serves update manifests and logs installs for Bowden Works WordPress plugins.

This plugin runs on `plugins.bowden.works` (the BW dist host) and is polled by every BW plugin installed on client sites.

## Endpoints

### `GET /wp-json/bw/v1/update-check?slug=bw-<slug>&version=<installed>`

Returns the latest manifest for a plugin. Logs the request to `wp_bw_update_log`.

**Query params:**
- `slug` (required): plugin slug like `bw-source-capture`
- `version` (optional): currently installed version on the client

**Response:** the sidecar `.meta.json` contents for the latest version of that slug.

**Example:**
```bash
curl 'https://plugins.bowden.works/wp-json/bw/v1/update-check?slug=bw-source-capture'
```

### `GET /wp-json/bw/v1/plugins`

Returns a list of all plugins with manifests on this host.

### `GET /wp-json/bw/v1/health`

Simple health check.

## How manifests are published

The `tools/release.sh` script on the dev host writes `.meta.json` sidecar files and zip files to `wp-content/uploads/plugin-updates/`:

```
wp-content/uploads/plugin-updates/
├── bw-source-capture-0.1.0.zip
├── bw-source-capture-0.1.0.meta.json
├── bw-source-capture-0.2.0.zip
└── bw-source-capture-0.2.0.meta.json
```

This plugin reads the highest-version `.meta.json` for a given slug on each request and returns it.

## Database

Creates table `wp_bw_update_log` with columns for plugin slug, site URL, versions, WP/PHP version, IP, user agent, and timestamp.

## Security

- REST endpoint is public by design (client sites must reach it unauthenticated).
- Input sanitization via `sanitize_key`, slug regex validation.
- No user-controlled file paths; manifests are loaded only from the uploads/plugin-updates directory.
- IP extracted from trusted proxy headers (Cloudflare / Caddy).

## Version

0.1.0 (MVP): REST endpoints + logging table, no admin dashboard yet. Admin dashboard coming in 0.2.0 (Phase 3b).
