# SEO Sidecar
> Inject up-to-date Schema.org JSON-LD into your nginx-served sites with **zero crons, zero site file changes, always fresh.** A FastAPI sidecar + nginx SSI integration that lets an upstream AI/automation push schemas to your hosting machine in real time.
[](LICENSE)
[](https://www.python.org/downloads/)
[](https://fastapi.tiangolo.com/)
[](https://nginx.org/)
## Why this exists
If you host static or server-rendered HTML through nginx and want **fresh, dynamic Schema.org JSON-LD** in every page's `
` without redeploying the site every time a schema changes, you have three bad options:
1. **Edit the HTML files** every time → site redeployment per schema change
2. **Cron job that rebuilds + redeploys** → stale between cron runs, slow propagation
3. **Server-side rendering** → couples schema generation to your rendering tier
SEO Sidecar is the fourth option. An upstream process (your AI, your CMS, your trend analyzer, whatever) pushes JSON-LD to a tiny FastAPI service on your nginx host. nginx pulls the schema in via SSI on every page load. Schema updates take effect **instantly** with no site redeployment, no cron lag, no rendering coupling.
Built in production for [ThatDevPro](https://www.thatdevpro.com)'s network of nine client websites.
## Architecture
```
(your AI publishing source)
│
│ Push over Tailscale (event-driven)
│ POST /update/{site} with JSON-LD
│
▼
┌──────────────────────────────────────────┐
│ (your nginx host) │
│ │
│ seo-sidecar (localhost:9090) │
│ ├── SQLite: /var/lib/seo-sidecar/ │
│ ├── Receives pushes from upstream │
│ ├── Serves schemas to nginx │
│ └── Keeps history + versioning │
│ │
│ nginx │
│ ├── SSI include on every page load │
│ ├── Fetches from localhost:9090 │
│ ├── 5min proxy_cache (configurable) │
│ └── 1s timeout (pages never stall) │
│ │
│ 9 hosted sites │
│ └── Each gets dynamic JSON-LD in │
└──────────────────────────────────────────┘
```
## How It Works
1. **Your upstream service decides** a schema needs updating (trend shift, new FAQ, competitor gap)
2. **Upstream pushes** the new JSON-LD to `http://:9090/update/{site}` over Tailscale
3. **Sidecar stores** it in SQLite with version history
4. **nginx SSI** fetches from `localhost:9090/schema/{site}` on every HTML page load
5. **Schema appears** in the `` of the site — search engines and AI crawlers see it immediately
No cron jobs. No file writes. No site deployments. Upstream controls schema state remotely.
## Files
| File | Where It Goes | Purpose |
|------|--------------|---------|
| `sidecar.py` | `/opt/seo-sidecar/` on the sidecar host | FastAPI service — receives pushes, serves to nginx |
| `nginx-seo-sidecar.conf` | `/etc/nginx/conf.d/` on the sidecar host | Upstream + hostname map |
| `seo-inject.conf` | `/etc/nginx/snippets/` on the sidecar host | Drop-in include for server blocks |
| `megamind_push.py` | upstream nodes | CLI push client |
| `seo_client.go` | Go push client | Go push client for integration |
| `seo-sidecar.service` | `/etc/systemd/system/` on the sidecar host | systemd unit |
| `deploy-sidecar.sh` | Run once on the sidecar host | Full automated deployment |
## Deploy on the sidecar host
```bash
# Copy files to your sidecar host
scp -r seo-sidecar/ :/tmp/seo-sidecar/
# SSH in and deploy
ssh
cd /tmp/seo-sidecar
sudo bash deploy-sidecar.sh
```
Then add one line to each nginx server block:
```nginx
include /etc/nginx/snippets/seo-inject.conf;
```
Reload nginx:
```bash
nginx -t && systemctl reload nginx
```
## Push from your upstream
```bash
# Push single site
python megamind_push.py push thatdeveloperguy
# Push all 9 sites
python megamind_push.py push-all
# Bulk push (single HTTP request)
python megamind_push.py push-bulk
# Check what's live
python megamind_push.py status
# View version history
python megamind_push.py history thatdeveloperguy
```
Or from Go:
```go
client := seo.NewClient("http://:9090", "your-token")
client.Push("thatdeveloperguy", jsonldString)
```
## API Endpoints
### Push (MEGAMIND → Sidecar)
- `POST /update/{site}` — Push schema for one site
- `POST /update-bulk` — Push schemas for multiple sites
- `DELETE /schema/{site}` — Remove a schema
### Serve (nginx → Sidecar)
- `GET /schema/{site}` — Returns `