Sync your WordPress posts to standard.site records on your PDS
1# Wireservice
2
3A WordPress plugin that publishes your posts and pages to the [AT Protocol](https://atproto.com) using the [standard.site](https://standard.site) lexicons (`site.standard.publication` and `site.standard.document`).
4
5## Requirements
6
7- PHP 8.3+
8- WordPress 6.4+
9- Composer
10
11## Dev Installation
12
131. Clone or download this repository into your `wp-content/plugins/` directory.
142. Install dependencies:
15 ```bash
16 composer install
17 ```
183. Activate the plugin in WordPress under **Plugins**.
19
20## Setup
21
221. Go to **Settings > Wireservice**.
232. Connect your AT Protocol account via the OAuth flow.
243. Configure your **Publication** settings (name, description, icon, theme colors) and sync to AT Protocol.
254. Enable **Document** syncing and configure how post titles, descriptions, and cover images are sourced.
26
27## How It Works
28
29### Publication
30
31Your WordPress site is represented as a `site.standard.publication` record on AT Protocol. The plugin syncs site-level metadata including:
32
33- **Name** — from WordPress site title, Yoast SEO, or a custom value
34- **Description** — from WordPress tagline, Yoast SEO, or a custom value
35- **Icon** — from WordPress site icon or a custom upload
36- **Theme colors** — background, foreground, accent, and accent foreground. NOTE: these are used by other ATProto platforms to style your content, not on your WordPress site.
37- **Discoverability** — opt in or out of discovery feeds. NOTE: these are used by other ATProto platforms to show your publication in algorithmic feeds, not on your WordPress site.
38
39The plugin also serves a `.well-known/site.standard.publication` endpoint that returns the AT-URI of your publication record.
40
41### Documents
42
43When document syncing is enabled, published posts and pages are automatically synced as `site.standard.document` records. Each document includes:
44
45- Title and description (configurable source)
46- Cover image (featured image, Yoast SEO image, or custom)
47- Publication date and last-updated date
48- Relative path (permalink)
49- Tags and categories
50- Optionally, full plain-text content
51
52Documents are created on publish, updated on edit, and deleted when a post is trashed, deleted, or unpublished.
53
54A `<link rel="site.standard.document">` tag is added to the `<head>` of each synced post for verification.
55
56Note that Wireservice does not have a content lexicon yet. This is in development.
57
58### Per-Post Overrides
59
60A **Wireservice** meta box appears on the post editor, allowing per-post overrides for:
61
62- Title source
63- Description source
64- Cover image source
65- Whether to include full content
66
67## Yoast SEO Integration
68
69When Yoast SEO is active, additional source options become available for both publication and document settings:
70
71- **Publication**: Yoast organization name, website name, homepage meta description
72- **Documents**: Yoast SEO title, social title, X title, meta description, social description, X description, social image, X image
73
74## Self-Hosting the OAuth Service
75
76Wireservice authenticates with AT Protocol through an external OAuth service. By default it uses `https://aip.wireservice.net`, but you can run your own instance using [AIP](https://github.com/graze-social/aip), a high-performance OAuth 2.1 authorization server with native AT Protocol integration.
77
78### Running AIP
79
80AIP requires Rust 1.87+. To run locally:
81
821. Generate an OAuth signing key with `goat`: `goat key generate -t p256`. Save the public and private keys somewhere safe.
83
842. Clone AIP: `git clone https://github.com/graze-social/aip.git`
85
863. Setup environment variables:
87
88```
89EXTERNAL_BASE=https://your-domain.com
90DPOP_NONCE_SEED=$(openssl rand -hex 32)
91STORAGE_BACKEND=sqlite
92ATPROTO_OAUTH_SIGNING_KEYS=`did:key:${YOUR_PRIVATE_KEY}`
93OAUTH_SIGNING_KEYS=`did:key:${YOUR_PRIVATE_KEY}`
94ENABLE_CLIENT_API=true
95OAUTH_SUPPORTED_SCOPES="atproto:atproto atproto:repo:site.standard.publication atproto:repo:site.standard.document
96atproto:blob:*/*"
97```
98
994. Run AIP: `cargo run --bin aip`
100
101Or with Docker:
102
103```bash
104docker build -t aip .
105docker run -p 8080:8080 \
106 // all of the above env vars here
107 aip
108```
109
110For production, use the `postgres` storage backend instead of `sqlite`. Depending on your hosting environment, you may need to manually set the `DNS_NAMESERVERS` env var so that your AIP service can resolve handles properly. (Wireservice uses `8.8.8.8,1.1.1.1`).
111
112### Configuring Wireservice
113
114Once your AIP instance is running, update the OAuth Service URL in WordPress:
115
1161. Go to **Settings > Wireservice**.
1172. Set the **OAuth Service URL** to your AIP instance (e.g., `https://your-domain.com`).
118
119This is stored as the `wireservice_oauth_url` option and can also be set programmatically:
120
121```php
122update_option('wireservice_oauth_url', 'https://your-domain.com');
123```
124
125## Filters
126
127```php
128// Customize which post types are synced (default: post, page)
129add_filter('wireservice_syncable_post_types', function ($types) {
130 $types[] = 'custom_post_type';
131 return $types;
132});
133
134// Control whether a specific post should sync
135add_filter('wireservice_should_sync_post', function ($should_sync, $post) {
136 return $should_sync;
137}, 10, 2);
138```
139
140## License
141
142[AGPL 3.0](LICENSE.md)