From f1dc155a0f39f67c4d8f9ad7ff02ea95bcd78173 Mon Sep 17 00:00:00 2001 From: Louis King Date: Fri, 17 Apr 2026 22:38:15 +0100 Subject: [PATCH] Move Webhooks and Custom Content sections to dedicated docs --- .agents/skills/docs-sync/SKILL.md | 2 + .../references/documentation-checklist.md | 35 ++++++- AGENTS.md | 51 +--------- README.md | 87 +---------------- docs/content.md | 96 +++++++++++++++++++ docs/webhooks.md | 74 ++++++++++++++ 6 files changed, 209 insertions(+), 136 deletions(-) create mode 100644 docs/content.md create mode 100644 docs/webhooks.md diff --git a/.agents/skills/docs-sync/SKILL.md b/.agents/skills/docs-sync/SKILL.md index 0f2bc0b..a4c6dbc 100644 --- a/.agents/skills/docs-sync/SKILL.md +++ b/.agents/skills/docs-sync/SKILL.md @@ -41,6 +41,8 @@ The following files are the documentation targets. All must be kept in sync: | `docs/letsmesh.md` | LetsMesh packet decoding: normalization, channel keys, message handling | | `docs/hosting/nginx-proxy-manager.md` | Nginx Proxy Manager admin authentication setup guide | | `docs/seeding.md` | Seed data: node tags and members YAML format, directory structure, import process | +| `docs/webhooks.md` | Webhook configuration: URLs, secrets, retries, payload format | +| `docs/content.md` | Custom content: markdown pages, media files, logos, frontmatter fields | | `docs/i18n.md` | Translation reference: all i18n keys, variable interpolation, translation tips | | `.env.example` | Example environment file with comments and defaults | | `SCHEMAS.md` | Event JSON schemas and database column mappings | diff --git a/.agents/skills/docs-sync/references/documentation-checklist.md b/.agents/skills/docs-sync/references/documentation-checklist.md index d019664..b4c88a5 100644 --- a/.agents/skills/docs-sync/references/documentation-checklist.md +++ b/.agents/skills/docs-sync/references/documentation-checklist.md @@ -10,7 +10,7 @@ README.md contains env var tables grouped by component. For each table: - [ ] **Common Settings table** — every var from `CommonSettings` is listed with correct default - [ ] **Collector Settings table** — every collector-specific var from `CollectorSettings` is listed -- [ ] **Webhook table** — all 11 webhook vars listed (6 URL/secret pairs + timeout + retries + backoff) +- [ ] **Webhook reference** — webhook config links to `docs/webhooks.md` - [ ] **Data Retention table** — all retention and node cleanup vars listed - [ ] **API Settings table** — all API-specific vars from `APISettings` listed - [ ] **Web Dashboard Settings table** — all web-specific vars from `WebSettings` listed @@ -56,7 +56,8 @@ For each variable in each table: - [ ] `src/meshcore_hub/` structure matches actual layout - [ ] Seed data directory structure (`node_tags.yaml`, `members.yaml`) documented in `docs/seeding.md` -- [ ] Custom content directory structure (`pages/`, `media/`) documented +- [ ] Custom content directory structure (`pages/`, `media/`) documented in `docs/content.md` +- [ ] Webhook configuration documented in `docs/webhooks.md` - [ ] Translation files location (`src/meshcore_hub/web/static/locales/`) documented - [ ] Translation reference guide (`docs/i18n.md`) linked from README and AGENTS.md - [ ] No references to removed files (PLAN.md, TASKS.md) @@ -100,6 +101,8 @@ AGENTS.md has a "Key variables" subsection under "Environment Variables". Verify - [ ] References to `SCHEMAS.md` — should remain (file exists) - [ ] References to `docs/upgrading.md` — should remain (file exists) - [ ] References to `docs/letsmesh.md` — should remain (file exists) +- [ ] References to `docs/webhooks.md` — should remain (file exists) +- [ ] References to `docs/content.md` — should remain (file exists) ## 3. docs/upgrading.md @@ -192,6 +195,34 @@ docs/i18n.md is a comprehensive reference for translators. Verify: - [ ] No stale translation keys documented (removed from `en.json`) - [ ] Translation tips are accurate for current i18n system +## 3f. docs/webhooks.md + +### Webhook Configuration Documentation + +docs/webhooks.md documents the webhook configuration, URL routing logic, and payload format. Verify: + +- [ ] All 11 webhook environment variables listed with correct defaults and descriptions +- [ ] URL routing rules documented correctly (MESSAGE_URL as default, CHANNEL/DIRECT overrides) +- [ ] Secret header mechanism documented (`X-Webhook-Secret`) +- [ ] Retry behavior documented (max retries, exponential backoff) +- [ ] Payload format JSON example matches `webhook.py` dispatcher output +- [ ] Event types listed match actual webhook event types +- [ ] Configuration examples use correct env var names + +## 3g. docs/content.md + +### Custom Content Documentation + +docs/content.md documents the custom content system for the web dashboard. Verify: + +- [ ] Directory structure shows `pages/` and `media/` subdirectories correctly +- [ ] Custom logo options (`logo.svg`, `logo-invert.svg`) documented with behavior per theme +- [ ] Frontmatter field table matches `pages.py` parser expectations +- [ ] Default values for frontmatter fields are correct (title, slug, menu_order) +- [ ] Setup examples create valid markdown pages +- [ ] Docker volume mounting instructions match `docker-compose.yml` bind mount config +- [ ] `CONTENT_HOME` default value documented correctly + ## 4. .env.example ### Section Structure diff --git a/AGENTS.md b/AGENTS.md index a12b378..4e61420 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -641,31 +641,7 @@ ${SEED_HOME}/ └── members.yaml # Network members list ``` -**Custom Content (`CONTENT_HOME`)** - Contains custom pages and media for the web dashboard: -``` -${CONTENT_HOME}/ -├── pages/ # Custom markdown pages -│ ├── about.md # Example: About page (/pages/about) -│ ├── faq.md # Example: FAQ page (/pages/faq) -│ └── getting-started.md # Example: Getting Started (/pages/getting-started) -└── media/ # Custom media files - └── images/ - ├── logo.svg # Full-color custom logo (default) - └── logo-invert.svg # Monochrome custom logo (darkened in light mode) -``` - -Pages use YAML frontmatter for metadata: -```markdown ---- -title: About Us # Browser tab title and nav link (not rendered on page) -slug: about # URL path (default: filename without .md) -menu_order: 10 # Nav sort order (default: 100, lower = earlier) ---- - -# About Our Network - -Markdown content here (include your own heading)... -``` +**Custom Content (`CONTENT_HOME`)** - Custom pages and media for the web dashboard. See [docs/content.md](docs/content.md) for directory structure, frontmatter fields, and setup guide. **Runtime Data (`DATA_HOME`)** - Contains runtime data (gitignored): ``` @@ -696,21 +672,7 @@ docker compose -f docker-compose.yml -f docker-compose.dev.yml --profile seed up ### Webhook Configuration -The collector supports forwarding events to external HTTP endpoints: - -| Variable | Description | -|----------|-------------| -| `WEBHOOK_ADVERTISEMENT_URL` | Webhook for node advertisement events | -| `WEBHOOK_ADVERTISEMENT_SECRET` | Secret sent as `X-Webhook-Secret` header | -| `WEBHOOK_MESSAGE_URL` | Webhook for all message events (channel + direct) | -| `WEBHOOK_MESSAGE_SECRET` | Secret for message webhook | -| `WEBHOOK_CHANNEL_MESSAGE_URL` | Override for channel messages only | -| `WEBHOOK_CHANNEL_MESSAGE_SECRET` | Secret for channel message webhook | -| `WEBHOOK_DIRECT_MESSAGE_URL` | Override for direct messages only | -| `WEBHOOK_DIRECT_MESSAGE_SECRET` | Secret for direct message webhook | -| `WEBHOOK_TIMEOUT` | Request timeout (default: 10.0s) | -| `WEBHOOK_MAX_RETRIES` | Max retries on failure (default: 3) | -| `WEBHOOK_RETRY_BACKOFF` | Exponential backoff multiplier (default: 2.0) | +The collector supports forwarding events to external HTTP endpoints with configurable URLs, secrets, retries, and timeouts. See [docs/webhooks.md](docs/webhooks.md) for the full configuration reference, URL routing logic, and payload format. ### Data Retention / Cleanup Configuration @@ -754,15 +716,6 @@ meshcore-hub collector cleanup --retention-days 30 --dry-run meshcore-hub collector cleanup --retention-days 30 ``` -Webhook payload structure: -```json -{ - "event_type": "advertisement", - "public_key": "abc123...", - "payload": { ... } -} -``` - ## Troubleshooting ### Common Issues diff --git a/README.md b/README.md index be808fe..585d2ef 100644 --- a/README.md +++ b/README.md @@ -322,31 +322,7 @@ For details on how the collector normalizes and decodes LetsMesh packets, see [d ### Webhooks -The collector can forward certain events to external HTTP endpoints: - -| Variable | Default | Description | -| -------------------------------- | -------- | ---------------------------------------- | -| `WEBHOOK_ADVERTISEMENT_URL` | _(none)_ | Webhook URL for advertisement events | -| `WEBHOOK_ADVERTISEMENT_SECRET` | _(none)_ | Secret sent as `X-Webhook-Secret` header | -| `WEBHOOK_MESSAGE_URL` | _(none)_ | Webhook URL for all message events | -| `WEBHOOK_MESSAGE_SECRET` | _(none)_ | Secret for message webhook | -| `WEBHOOK_CHANNEL_MESSAGE_URL` | _(none)_ | Override URL for channel messages only | -| `WEBHOOK_CHANNEL_MESSAGE_SECRET` | _(none)_ | Secret for channel message webhook | -| `WEBHOOK_DIRECT_MESSAGE_URL` | _(none)_ | Override URL for direct messages only | -| `WEBHOOK_DIRECT_MESSAGE_SECRET` | _(none)_ | Secret for direct message webhook | -| `WEBHOOK_TIMEOUT` | `10.0` | Request timeout in seconds | -| `WEBHOOK_MAX_RETRIES` | `3` | Max retry attempts on failure | -| `WEBHOOK_RETRY_BACKOFF` | `2.0` | Exponential backoff multiplier | - -Webhook payload format: - -```json -{ - "event_type": "advertisement", - "public_key": "abc123...", - "payload": { ... event data ... } -} -``` +The collector can forward events (advertisements, messages) to external HTTP endpoints via webhooks with configurable URLs, secrets, retries, and timeouts. See [docs/webhooks.md](docs/webhooks.md) for the full configuration reference and payload format. ### Data Retention @@ -420,66 +396,7 @@ Control which pages are visible in the web dashboard. Disabled features are full ### Custom Content -The web dashboard supports custom content including markdown pages and media files. Content is organized in subdirectories: - -Custom logo options: - -- `logo.svg` — full-color logo, displayed as-is in both themes (no automatic darkening) -- `logo-invert.svg` — monochrome/two-tone logo, automatically darkened in light mode for visibility - -``` -content/ -├── pages/ # Custom markdown pages -│ └── about.md -└── media/ # Custom media files - └── images/ - ├── logo.svg # Full-color custom logo (default) - └── logo-invert.svg # Monochrome custom logo (darkened in light mode) -``` - -**Setup:** - -```bash -# Create content directory structure -mkdir -p content/pages content/media - -# Create a custom page -cat > content/pages/about.md << 'EOF' ---- -title: About Us -slug: about -menu_order: 10 ---- - -# About Our Network - -Welcome to our MeshCore mesh network! - -## Getting Started - -1. Get a compatible LoRa device -2. Flash MeshCore firmware -3. Configure your radio settings -EOF -``` - -**Frontmatter fields:** -| Field | Default | Description | -|-------|---------|-------------| -| `title` | Filename titlecased | Browser tab title and navigation link text (not rendered on page) | -| `slug` | Filename without `.md` | URL path (e.g., `about` → `/pages/about`) | -| `menu_order` | `100` | Sort order in navigation (lower = earlier) | - -The markdown content is rendered as-is, so include your own `# Heading` if desired. - -Pages automatically appear in the navigation menu and sitemap. With Docker, mount the content directory: - -```yaml -# docker-compose.yml (already configured)volumes: - - ${CONTENT_HOME:-./content}:/content:ro -environment: - - CONTENT_HOME=/content -``` +The web dashboard supports custom markdown pages and media files (including custom logos) served from a configurable content directory. See [docs/content.md](docs/content.md) for the full setup guide including directory structure, frontmatter fields, and Docker volume mounting. ## Seed Data diff --git a/docs/content.md b/docs/content.md new file mode 100644 index 0000000..b7afe21 --- /dev/null +++ b/docs/content.md @@ -0,0 +1,96 @@ +# Custom Content + +The web dashboard supports custom content including markdown pages and media files. Content is served from the `CONTENT_HOME` directory (default: `./content`). + +## Directory Structure + +``` +content/ +├── pages/ # Custom markdown pages +│ └── about.md +└── media/ # Custom media files + └── images/ + ├── logo.svg # Full-color custom logo (default) + └── logo-invert.svg # Monochrome custom logo (darkened in light mode) +``` + +## Custom Logos + +The web dashboard supports custom logo images placed in `media/images/`: + +- `logo.svg` — full-color logo, displayed as-is in both themes (no automatic darkening) +- `logo-invert.svg` — monochrome/two-tone logo, automatically darkened in light mode for visibility + +If no custom logos are provided, the default MeshCore Hub logos are used. + +## Markdown Pages + +Custom pages are written in Markdown with optional YAML frontmatter for metadata. Pages automatically appear in the navigation menu and sitemap. + +### Setup + +```bash +# Create content directory structure +mkdir -p content/pages content/media + +# Create a custom page +cat > content/pages/about.md << 'EOF' +--- +title: About Us +slug: about +menu_order: 10 +--- + +# About Our Network + +Welcome to our MeshCore mesh network! + +## Getting Started + +1. Get a compatible LoRa device +2. Flash MeshCore firmware +3. Configure your radio settings +EOF +``` + +### Frontmatter Fields + +Pages use YAML frontmatter for metadata: + +```markdown +--- +title: About Us # Browser tab title and nav link (not rendered on page) +slug: about # URL path (default: filename without .md) +menu_order: 10 # Nav sort order (default: 100, lower = earlier) +--- + +# About Our Network + +Markdown content here (include your own heading)... +``` + +| Field | Default | Description | +|-------|---------|-------------| +| `title` | Filename titlecased | Browser tab title and navigation link text (not rendered on page) | +| `slug` | Filename without `.md` | URL path (e.g., `about` → `/pages/about`) | +| `menu_order` | `100` | Sort order in navigation (lower = earlier) | + +The markdown content is rendered as-is, so include your own `# Heading` if desired. + +## Docker Configuration + +With Docker, mount the content directory as a read-only volume. This is already configured in `docker-compose.yml`: + +```yaml +volumes: + - ${CONTENT_HOME:-./content}:/content:ro +environment: + - CONTENT_HOME=/content +``` + +To customize the content path, set `CONTENT_HOME` in your `.env` file: + +```bash +# .env +CONTENT_HOME=./content +``` diff --git a/docs/webhooks.md b/docs/webhooks.md new file mode 100644 index 0000000..7404cf1 --- /dev/null +++ b/docs/webhooks.md @@ -0,0 +1,74 @@ +# Webhooks + +The collector can forward certain events to external HTTP endpoints. This is useful for integrating MeshCore Hub with external systems, notification services, or custom processing pipelines. + +## Configuration + +| Variable | Default | Description | +| -------------------------------- | -------- | ---------------------------------------- | +| `WEBHOOK_ADVERTISEMENT_URL` | _(none)_ | Webhook URL for advertisement events | +| `WEBHOOK_ADVERTISEMENT_SECRET` | _(none)_ | Secret sent as `X-Webhook-Secret` header | +| `WEBHOOK_MESSAGE_URL` | _(none)_ | Webhook URL for all message events | +| `WEBHOOK_MESSAGE_SECRET` | _(none)_ | Secret for message webhook | +| `WEBHOOK_CHANNEL_MESSAGE_URL` | _(none)_ | Override URL for channel messages only | +| `WEBHOOK_CHANNEL_MESSAGE_SECRET` | _(none)_ | Secret for channel message webhook | +| `WEBHOOK_DIRECT_MESSAGE_URL` | _(none)_ | Override URL for direct messages only | +| `WEBHOOK_DIRECT_MESSAGE_SECRET` | _(none)_ | Secret for direct message webhook | +| `WEBHOOK_TIMEOUT` | `10.0` | Request timeout in seconds | +| `WEBHOOK_MAX_RETRIES` | `3` | Max retry attempts on failure | +| `WEBHOOK_RETRY_BACKOFF` | `2.0` | Exponential backoff multiplier | + +### URL Routing + +- `WEBHOOK_MESSAGE_URL` receives **both** channel and direct message events unless overridden. +- `WEBHOOK_CHANNEL_MESSAGE_URL` overrides the URL for channel messages only. +- `WEBHOOK_DIRECT_MESSAGE_URL` overrides the URL for direct messages only. + +### Secrets + +Each webhook URL can optionally have a corresponding secret. When configured, the secret is sent as the `X-Webhook-Secret` HTTP header, allowing the receiving endpoint to verify the request origin. + +### Retries + +Failed webhook deliveries are retried with exponential backoff: + +- **`WEBHOOK_MAX_RETRIES`** — Maximum number of retry attempts (default: 3). +- **`WEBHOOK_RETRY_BACKOFF`** — Backoff multiplier applied between retries (default: 2.0). For example, with the default settings, retries occur at approximately 2s, 4s, and 8s. + +## Payload Format + +All webhooks send a JSON POST request with the following structure: + +```json +{ + "event_type": "advertisement", + "public_key": "abc123...", + "payload": { ... } +} +``` + +### Event Types + +| Event Type | Trigger | Payload Content | +| ----------------- | -------------------------------- | ---------------------------- | +| `advertisement` | Node advertisement received | Advertisement event data | +| `channel_message` | Channel message received | Channel message event data | +| `direct_message` | Direct message received | Direct message event data | + +### Example: Advertisement Webhook + +```bash +# .env +WEBHOOK_ADVERTISEMENT_URL=https://example.com/webhook +WEBHOOK_ADVERTISEMENT_SECRET=my-secret-key +``` + +### Example: Separate Channel and Direct Message Webhooks + +```bash +# .env +WEBHOOK_CHANNEL_MESSAGE_URL=https://example.com/channel-webhook +WEBHOOK_CHANNEL_MESSAGE_SECRET=channel-secret +WEBHOOK_DIRECT_MESSAGE_URL=https://example.com/direct-webhook +WEBHOOK_DIRECT_MESSAGE_SECRET=direct-secret +```