{"packages":[{"uri":"at://did:plc:jx7ku4di4csnx3sxohz374m7/com.emdashcms.experimental.package.profile/api-indonesia","cid":"bafyreihli6gmwoun32gb2qfvdgqy3neeje2nqgtvycqdlzpzwuijao5ola","did":"did:plc:jx7ku4di4csnx3sxohz374m7","slug":"api-indonesia","profile":{"$type":"com.emdashcms.experimental.package.profile","id":"at://did:plc:jx7ku4di4csnx3sxohz374m7/com.emdashcms.experimental.package.profile/api-indonesia","type":"emdash-plugin","license":"MIT","authors":[{"url":"https://apiindonesia.id","name":"API Indonesia","email":"contactmuhtadin@gmail.com"}],"security":[{"email":"contactmuhtadin@gmail.com"}],"name":"API Indonesia: Holidays & Prayer Times","description":"Indonesian public holidays, cuti bersama and daily prayer times on your EmDash dashboard and site, from API Indonesia.","keywords":["indonesia","holidays","prayer times","api","calendar"],"sections":{"description":"Show Indonesian public holidays, cuti bersama (joint leave days) and today's prayer times in your EmDash admin dashboard, and serve the same data as JSON to your site templates.\n\nData comes from [API Indonesia](https://apiindonesia.id), a REST API for Indonesian public data from official sources. Holidays follow the SKB 3 Menteri decree. Prayer times cover all 514 cities and regencies.\n\nWhat you get:\n\n- A dashboard widget, \"Indonesia today\", with today's prayer times for your city and the next public holidays.\n- Two public routes for your theme: `/_emdash/api/plugins/api-indonesia/holidays` and `/_emdash/api/plugins/api-indonesia/prayer-times`.\n- Responses are cached in plugin KV (holidays for up to 6 hours, prayer times per day), so visitors do not trigger a paid request on every page view.\n\nThe plugin only talks to `use.apiindonesia.id` and needs your own API Indonesia key.\n","installation":"1. Install the plugin from the EmDash plugin registry in your site's admin.\n2. Create an account at [dashboard.apiindonesia.id](https://dashboard.apiindonesia.id/register). New accounts get a one-time 3,000 credit bonus, no card needed. A successful request costs 2 credits; failed requests are not charged.\n3. Create an API key in the dashboard and paste it into **Plugins > API Indonesia** in your EmDash admin. The key is stored as an encrypted secret setting.\n4. Set the 4-digit city or regency code for prayer times (for example `3171` for Jakarta Pusat or `6171` for Pontianak). The full list is at `GET https://use.apiindonesia.id/api/v1/sholat/list-kota`.\n5. Add the \"Indonesia today\" widget to your dashboard, or fetch the public routes from your templates.\n"},"extensions":{"com.emdashcms.experimental.package.profileExtension":{"$type":"com.emdashcms.experimental.package.profileExtension","repository":"https://github.com/khmuhtadin/emdash-plugin-api-indonesia","releasePolicy":{"$type":"com.emdashcms.experimental.package.profileExtension#releasePolicy","requireProvenance":false}}},"lastUpdated":"2026-11-08T01:52:47.700Z","slug":"api-indonesia"},"indexedAt":"2026-11-08T01:52:55.219Z","labels":[{"ver":1,"src":"did:web:labels.emdashcms.com","uri":"at://did:plc:jx7ku4di4csnx3sxohz374m7/com.emdashcms.experimental.package.profile/api-indonesia","val":"listing-passed","cts":"2026-11-08T01:53:00.493Z","cid":"bafyreihli6gmwoun32gb2qfvdgqy3neeje2nqgtvycqdlzpzwuijao5ola"}],"handle":"apiindonesia.id","latestVersion":"0.1.0"},{"uri":"at://did:plc:bejyxkxwvbgbbuzmpqk5wmbg/com.emdashcms.experimental.package.profile/emdash-stripe","cid":"bafyreiab2rqkeypi6fdhk4npn6ltcxpyexavkwllpq2ju2x32qkrtyjpoy","did":"did:plc:bejyxkxwvbgbbuzmpqk5wmbg","slug":"emdash-stripe","profile":{"$type":"com.emdashcms.experimental.package.profile","id":"at://did:plc:bejyxkxwvbgbbuzmpqk5wmbg/com.emdashcms.experimental.package.profile/emdash-stripe","type":"emdash-plugin","license":"MIT","authors":[{"url":"https://jameswelbes.com","name":"James Welbes"}],"security":[{"email":"james.welbes@gmail.com"}],"description":"Stripe-powered ecommerce for EmDash: checkout sessions, order tracking, and email receipts.","extensions":{"com.emdashcms.experimental.package.profileExtension":{"$type":"com.emdashcms.experimental.package.profileExtension","repository":"https://github.com/welbinator/emdash-stripe","releasePolicy":{"$type":"com.emdashcms.experimental.package.profileExtension#releasePolicy","requireProvenance":false}}},"lastUpdated":"2026-11-07T23:59:53.467Z","slug":"emdash-stripe"},"indexedAt":"2026-11-08T00:00:49.278Z","labels":[{"ver":1,"src":"did:web:labels.emdashcms.com","uri":"at://did:plc:bejyxkxwvbgbbuzmpqk5wmbg/com.emdashcms.experimental.package.profile/emdash-stripe","val":"listing-passed","cts":"2026-11-08T00:01:04.565Z","cid":"bafyreiab2rqkeypi6fdhk4npn6ltcxpyexavkwllpq2ju2x32qkrtyjpoy"}],"handle":"jameswelbes.bsky.social","latestVersion":"0.1.0"},{"uri":"at://did:plc:ismjxguhz6v4xhywwowon2ym/com.emdashcms.experimental.package.profile/veprompts","cid":"bafyreidfyzvi5p3phofzyzhs7cfykbp45kwcr6pezxjqzsayxzc55dx62q","did":"did:plc:ismjxguhz6v4xhywwowon2ym","slug":"veprompts","profile":{"$type":"com.emdashcms.experimental.package.profile","id":"at://did:plc:ismjxguhz6v4xhywwowon2ym/com.emdashcms.experimental.package.profile/veprompts","type":"emdash-plugin","license":"MIT","authors":[{"url":"https://veprompts.com","name":"VePrompts","email":"cody@veduis.com"}],"security":[{"url":"https://veprompts.com/privacy-policy/","email":"cody@veduis.com"}],"name":"VePrompts AI Engineering Toolkit","description":"Exposes 1546 production prompts, 439 model pricing benchmarks, and 811 cataloged MCP servers directly inside EmDash CMS.","keywords":["prompts","pricing","mcp","benchmarks","agents"],"sections":{"changelog":"### Changelog\n\n## [1.0.0] - 2026-10-31\n\n- Initial release of VePrompts AI Engineering Toolkit for EmDash CMS.\n- Exposes `search`, `pricing`, `catalog`, and `health` endpoints.\n- Pure sandboxed V8 execution with zero tracking.","description":"# VePrompts AI Engineering Toolkit\n\nThe official VePrompts plugin for [EmDash CMS](https://emdashcms.com). Embed and query verified production AI prompts, frontier model pricing benchmarks, and Model Context Protocol (MCP) servers directly within your content workflows.\n\nMaintained by **[VePrompts](https://veprompts.com)** and **[Veduis](https://veduis.com)**.\n\n### Comprehensive AI Capabilities\n\n- **Production Prompt Library:** Access metadata and instructions from over 1546 verified prompts across coding, reasoning, and multimodal analysis. Browse the full library at [VePrompts Prompts](https://veprompts.com/prompts/).\n- **AI Model Pricing Benchmarks:** Compare current input and output token pricing across 439+ foundation models and 68 providers. Check live rates at [VePrompts Pricing](https://veprompts.com/pricing/).\n- **Model Context Protocol (MCP) Catalog:** Discover 811 cataloged MCP servers with client compatibility matrices and direct installation commands at [VePrompts MCP Servers](https://veprompts.com/mcp/servers/).\n- **Agent Workflows:** Explore standardized SKILL.md directives and multi-agent simulation blueprints at [VePrompts Skills](https://veprompts.com/skills/).\n\n### Trust Contract & Privacy\n\nVePrompts runs inside EmDash's secure workerd sandbox isolate. It operates purely in memory with zero invasive tracking or external data harvesting.\n\nLearn more at [https://veprompts.com/](https://veprompts.com/).","installation":"### Installation\n\nInstall VePrompts into your EmDash project:\n\n```bash\nnpm install @veduis.bsky.social/veprompts\n```\n\nAdd the plugin to your `emdash.config.ts`:\n\n```typescript\nimport veprompts from \"@veduis.bsky.social/veprompts\";\n\nexport default {\n  plugins: [veprompts],\n};\n```"},"extensions":{"com.emdashcms.experimental.package.profileExtension":{"$type":"com.emdashcms.experimental.package.profileExtension","repository":"https://github.com/Veduis/VePrompts","releasePolicy":{"$type":"com.emdashcms.experimental.package.profileExtension#releasePolicy","requireProvenance":false}}},"lastUpdated":"2026-11-07T23:35:16.634Z","slug":"veprompts"},"indexedAt":"2026-11-07T23:35:24.330Z","labels":[{"ver":1,"src":"did:web:labels.emdashcms.com","uri":"at://did:plc:ismjxguhz6v4xhywwowon2ym/com.emdashcms.experimental.package.profile/veprompts","val":"listing-passed","cts":"2026-11-07T23:35:29.442Z","cid":"bafyreidfyzvi5p3phofzyzhs7cfykbp45kwcr6pezxjqzsayxzc55dx62q"}],"handle":"veduis.bsky.social","latestVersion":"1.0.0"},{"uri":"at://did:plc:24gklxl5ljmwp3txk7tl32si/com.emdashcms.experimental.package.profile/randomizer","cid":"bafyreidjuzft64qtj56opbypi4wojzrjvyxcsuffk7hernpbeevrpo6ffy","did":"did:plc:24gklxl5ljmwp3txk7tl32si","slug":"randomizer","profile":{"$type":"com.emdashcms.experimental.package.profile","id":"at://did:plc:24gklxl5ljmwp3txk7tl32si/com.emdashcms.experimental.package.profile/randomizer","type":"emdash-plugin","license":"MIT","authors":[{"url":"https://github.com/alexlmiller","name":"Alex Miller"}],"security":[{"url":"https://github.com/alexlmiller/emdash-randomizer/security/advisories/new"}],"name":"Randomizer","description":"Send visitors to a random published entry, optionally scoped by category, tag or byline.","extensions":{"com.emdashcms.experimental.package.profileExtension":{"$type":"com.emdashcms.experimental.package.profileExtension","repository":"https://github.com/alexlmiller/emdash-randomizer","releasePolicy":{"$type":"com.emdashcms.experimental.package.profileExtension#releasePolicy","requireProvenance":false}}},"lastUpdated":"2026-11-07T18:34:56.908Z","slug":"randomizer"},"indexedAt":"2026-11-07T18:35:04.431Z","labels":[{"ver":1,"src":"did:web:labels.emdashcms.com","uri":"at://did:plc:24gklxl5ljmwp3txk7tl32si/com.emdashcms.experimental.package.profile/randomizer","val":"listing-passed","cts":"2026-11-07T18:35:10.234Z","cid":"bafyreidjuzft64qtj56opbypi4wojzrjvyxcsuffk7hernpbeevrpo6ffy"}],"handle":"alexmiller.net","latestVersion":"1.0.0"},{"uri":"at://did:plc:2qc3z64xt7csarggeudreqbl/com.emdashcms.experimental.package.profile/saascity","cid":"bafyreiey5inqbcvc6x456v2duzibpm7asrxjyu4jox7gyk3pdxolv4nfbi","did":"did:plc:2qc3z64xt7csarggeudreqbl","slug":"saascity","profile":{"$type":"com.emdashcms.experimental.package.profile","id":"at://did:plc:2qc3z64xt7csarggeudreqbl/com.emdashcms.experimental.package.profile/saascity","type":"emdash-plugin","license":"MIT","authors":[{"url":"https://saascity.io","name":"SaaSCity"}],"security":[{"url":"https://saascity.io/security","email":"support@saascity.io"}],"name":"SaaSCity","description":"See your SaaSCity listings, their review and launch state, and new upvotes from your EmDash admin.","keywords":["saascity","launch","directory","upvotes","saas"],"sections":{"faq":"### Do I need a SaaSCity listing?\n\nNo. You can connect an account with no listings. The page then links to the submit form.\n\n### What can the key do?\n\nIt can read your listings, read the upvotes on them, search live listings and start a draft. This plugin uses only the read calls. Nothing is listed or charged without you finishing the checkout on saascity.io.\n\n### How do I disconnect?\n\nOpen **API key** on the SaaSCity page and select **Disconnect**. Then revoke the key on saascity.io if you no longer use it.\n","security":"Report a security issue to support@saascity.io. Our policy is at https://saascity.io/security.\n\nThe plugin stores one value, your SaaSCity API key, as an encrypted EmDash secret setting. It talks only to `saascity.io` over HTTPS and sends the key in the `Authorization` header. It stores no visitor data and adds nothing to your public site.\n","changelog":"### 0.1.0\n\n- First release: connect with a SaaSCity API key, see your listings, their state, launch week and upvotes, and the latest upvotes.\n","description":"SaaSCity is a launch directory for SaaS products. This plugin brings your SaaSCity account into the EmDash admin.\n\n- See every listing you have on SaaSCity and where it is: awaiting payment, in review, scheduled, live, rejected or archived.\n- See the launch week and upvote count of each listing.\n- See the latest upvotes on your listings as they come in.\n\nThe plugin only reads your own account. It cannot submit, pay for, edit or publish anything.\n","installation":"1. Install the plugin on your EmDash site.\n2. On SaaSCity, open [Settings, API](https://saascity.io/dashboard/settings/api) and create a key. It starts with `scm_live_`.\n3. In the EmDash admin, open **SaaSCity**, paste the key and select **Connect**.\n\nThe key is stored as an encrypted secret setting, so your site needs `EMDASH_ENCRYPTION_KEY` set.\n"},"lastUpdated":"2026-11-07T18:21:20.535Z","slug":"saascity"},"indexedAt":"2026-11-07T18:21:48.865Z","labels":[{"ver":1,"src":"did:web:labels.emdashcms.com","uri":"at://did:plc:2qc3z64xt7csarggeudreqbl/com.emdashcms.experimental.package.profile/saascity","val":"listing-passed","cts":"2026-11-07T18:21:54.288Z","cid":"bafyreiey5inqbcvc6x456v2duzibpm7asrxjyu4jox7gyk3pdxolv4nfbi"}],"handle":"saascity.io","latestVersion":"0.1.0"},{"uri":"at://did:plc:dqfzcnruxn4gqgeu5fgeclec/com.emdashcms.experimental.package.profile/langrelay","cid":"bafyreieax3hl7qccqxkz7qpc7us2z6twkiikdjqeyrao22zpknd6botmi4","did":"did:plc:dqfzcnruxn4gqgeu5fgeclec","slug":"langrelay","profile":{"$type":"com.emdashcms.experimental.package.profile","id":"at://did:plc:dqfzcnruxn4gqgeu5fgeclec/com.emdashcms.experimental.package.profile/langrelay","type":"emdash-plugin","license":"MIT","authors":[{"url":"https://langrelay.com","name":"LangRelay"}],"security":[{"email":"security@langrelay.com"}],"name":"LangRelay: website translation","description":"Your EmDash site in every language, kept up to date as it changes. Translations go live safely, on search-friendly language URLs.","keywords":["translation","multilingual","localization","i18n","seo"],"extensions":{"com.emdashcms.experimental.package.profileExtension":{"$type":"com.emdashcms.experimental.package.profileExtension","repository":"https://github.com/langrelay/emdash","releasePolicy":{"$type":"com.emdashcms.experimental.package.profileExtension#releasePolicy","requireProvenance":false}}},"lastUpdated":"2026-11-07T17:32:04.281Z","slug":"langrelay"},"indexedAt":"2026-11-07T17:32:11.722Z","labels":[{"ver":1,"src":"did:web:labels.emdashcms.com","uri":"at://did:plc:dqfzcnruxn4gqgeu5fgeclec/com.emdashcms.experimental.package.profile/langrelay","val":"listing-passed","cts":"2026-11-07T17:32:16.853Z","cid":"bafyreieax3hl7qccqxkz7qpc7us2z6twkiikdjqeyrao22zpknd6botmi4"}],"handle":"langrelay.com","latestVersion":"0.0.0"},{"uri":"at://did:plc:g2hei4vcwndrdl3gdbb6np6c/com.emdashcms.experimental.package.profile/eventual","cid":"bafyreicomly6ss7lhcvldopqydobfmu5fgul544hqwuo5mlmpc4vyhlciq","did":"did:plc:g2hei4vcwndrdl3gdbb6np6c","slug":"eventual","profile":{"$type":"com.emdashcms.experimental.package.profile","id":"at://did:plc:g2hei4vcwndrdl3gdbb6np6c/com.emdashcms.experimental.package.profile/eventual","type":"emdash-plugin","license":"MIT","authors":[{"name":"Jaap Vermeulen","email":"jaap.vermeulen@gmail.com"}],"security":[{"email":"jaap.vermeulen@gmail.com"}],"name":"Eventual","description":"Native EmDash events with rich text, localization, friendly recurrence, saved venues and multilingual calendar feeds.","keywords":["events","calendar","event management","iCalendar","venues"],"sections":{"changelog":"## 0.17.0\n\nMulti-locale UI dictionaries, universal 1-click calendar links, and single-event query hydration: Complete German (`de`), Dutch (`nl`), and Spanish (`es`) UI localization dictionaries with regional fallback. Universal 1-click links for Google Calendar, Outlook Web, Office 365, and direct Apple/desktop iCal downloads on all `PublicEvent` payloads and the `<EventList showCalendarLinks={true} />` Astro component. Targeted `eventId` query support on `publicEvents`. Full backwards compatibility and EmDash **>=1.2.0 <2.0.0** support preserved. [Release notes](https://github.com/Vermeulen-Solutions/eventual-emdash-sandbox/blob/main/docs/release-notes-0.17.0.md).\n\n## Earlier releases\n\n- **0.16.0:** parallelized public feed resolution, cache headers for public media, and direct `imageMedia` metadata for responsive Astro `<Image>`.\n- **0.15.0:** opt-in public feed cache evidence: actual collection dependencies, empty-list invalidation and absolute expiry.\n- **0.14.1:** EmDash 1.4 editor compatibility; no runtime or permission changes.\n- **0.14.0:** public legacy storage reads require explicit opt-in; native collections stay authoritative.\n- **0.13.0:** native rich content, translations, drafts, directories and publication.\n\n[Full changelog](https://github.com/Vermeulen-Solutions/eventual-emdash-sandbox/blob/main/CHANGELOG.md).\n","installation":"# Eventual 0.17.0\r\n\r\nRequires EmDash **>=1.2.0 <2.0.0** for event management. **1.4.0 is recommended** for nested forms and Cloudflare plugin mutations, and required for the optional cache integration.\r\n\r\nIn **Plugins → Registry**, find Eventual by **vermeulen.solutions**, review permissions and install or update. Version 0.17.0 adds no capabilities or MCP tools. Enable intended tools separately under **Agent access**.\r\n\r\nOpen **Eventual → Settings**, select compatible event, venue and organizer collections, then **Save settings**. Installation does not create schemas or migrate content. Site builders: [collection setup](https://github.com/Vermeulen-Solutions/eventual-emdash-sandbox/blob/main/docs/collection-setup.md).\r\n\r\nSave an announcement, open **Dates, venue & repeat**, save changed sections, reload, then publish in EmDash. [Editor guide](https://github.com/Vermeulen-Solutions/eventual-emdash-sandbox/blob/main/docs/user-guide.md).\r\n\r\nKeep **Show old events saved inside the plugin** off for native collections. Enable it only to serve legacy plugin data without a connected collection. Upgrades from 0.12.x or earlier need a backup and [native migration](https://github.com/Vermeulen-Solutions/eventual-emdash-sandbox/blob/main/docs/upgrade-0.13.0.md).\r\n\r\nYour site builder connects visitor pages. **Caching stays opt-in**: 0.17.0 supplies dependencies and a maximum 15-second expiry, not site-wide cache rules. Use matching source helpers, explicit eligibility and no stale serving. Read the [integration guide and host limitations](https://github.com/Vermeulen-Solutions/eventual-emdash-sandbox/blob/main/docs/public-feed-caching.md) before enabling it.\r\n"},"extensions":{"com.emdashcms.experimental.package.profileExtension":{"$type":"com.emdashcms.experimental.package.profileExtension","repository":"https://github.com/Vermeulen-Solutions/eventual-emdash-sandbox","releasePolicy":{"$type":"com.emdashcms.experimental.package.profileExtension#releasePolicy","requireProvenance":false}}},"lastUpdated":"2026-11-07T15:33:54.843Z","slug":"eventual"},"indexedAt":"2026-11-07T15:34:01.549Z","labels":[{"ver":1,"src":"did:web:labels.emdashcms.com","uri":"at://did:plc:g2hei4vcwndrdl3gdbb6np6c/com.emdashcms.experimental.package.profile/eventual","val":"listing-overridden","cts":"2026-09-27T13:21:57.101Z","cid":"bafyreiemauxipdfgvpq2pa3zuhzitb5guull2x5czjaqwavcw5dxgdwsu4"},{"ver":1,"src":"did:web:labels.emdashcms.com","uri":"at://did:plc:g2hei4vcwndrdl3gdbb6np6c/com.emdashcms.experimental.package.profile/eventual","val":"listing-review","cts":"2026-09-27T13:21:57.101Z","cid":"bafyreiemauxipdfgvpq2pa3zuhzitb5guull2x5czjaqwavcw5dxgdwsu4","neg":true},{"ver":1,"src":"did:web:labels.emdashcms.com","uri":"at://did:plc:g2hei4vcwndrdl3gdbb6np6c/com.emdashcms.experimental.package.profile/eventual","val":"listing-passed","cts":"2026-11-07T15:34:07.298Z","cid":"bafyreicomly6ss7lhcvldopqydobfmu5fgul544hqwuo5mlmpc4vyhlciq"}],"handle":"vermeulen.solutions","latestVersion":"0.17.0"},{"uri":"at://did:web:pds.aranx.com/com.emdashcms.experimental.package.profile/aranx","cid":"bafyreib4qtp3uuk2pmdiplo6d6gg5i52aorbdzirfpodnsdccdjpqgn2se","did":"did:web:pds.aranx.com","slug":"aranx","profile":{"$type":"com.emdashcms.experimental.package.profile","id":"at://did:web:pds.aranx.com/com.emdashcms.experimental.package.profile/aranx","type":"emdash-plugin","license":"MIT","authors":[{"url":"https://aranx.com","name":"Aranx"}],"security":[{"email":"security@aranx.com"}],"name":"Aranx","description":"Aranx writes SEO blog articles. This plugin saves each one as an EmDash post with its image and categories, and publishes it on request.","keywords":["seo","blog","ai","content","aranx"],"extensions":{"com.emdashcms.experimental.package.profileExtension":{"$type":"com.emdashcms.experimental.package.profileExtension","repository":"https://github.com/aranx-ai/emdash-plugin","releasePolicy":{"$type":"com.emdashcms.experimental.package.profileExtension#releasePolicy","approvers":["did:web:pds.aranx.com"],"confirmation":"escalation-only","requireProvenance":true}}},"lastUpdated":"2026-11-07T10:15:17.877Z","slug":"aranx"},"indexedAt":"2026-11-07T10:15:47.781Z","labels":[{"ver":1,"src":"did:web:labels.emdashcms.com","uri":"at://did:web:pds.aranx.com/com.emdashcms.experimental.package.profile/aranx","val":"listing-passed","cts":"2026-11-07T10:16:06.830Z","cid":"bafyreib4qtp3uuk2pmdiplo6d6gg5i52aorbdzirfpodnsdccdjpqgn2se"}],"handle":"aranx.com","latestVersion":"0.1.5"},{"uri":"at://did:plc:tsp7az5h6qsqjzqsgz37wonx/com.emdashcms.experimental.package.profile/freeform","cid":"bafyreihxjlubvbggxppcm3wtnkq33cl2ftj4gqgmln3f4jyv7supiqubhq","did":"did:plc:tsp7az5h6qsqjzqsgz37wonx","slug":"freeform","profile":{"$type":"com.emdashcms.experimental.package.profile","id":"at://did:plc:tsp7az5h6qsqjzqsgz37wonx/com.emdashcms.experimental.package.profile/freeform","type":"emdash-plugin","license":"MIT","authors":[{"url":"https://solspace.com","name":"Solspace"}],"security":[{"email":"support@solspace.com"}],"name":"Freeform","description":"Build forms in EmDash with an AI-assisted builder, spam scoring, email notifications, webhooks, and CSV exports.","keywords":["forms","form builder","submissions","spam protection","webhooks"],"sections":{"faq":"# FAQ\n\n**Do I need an API key?**\nOnly for AI features (Generate with AI, spam scoring, briefs). Add an OpenAI\nkey under Freeform → Settings → AI & spam. Forms, email, and webhooks work\nwithout one.\n\n**Where do I configure SMTP / Resend / SendGrid?**\nIn **EmDash → Settings → Email**, not Freeform. Freeform decides what to send;\nEmDash’s email provider delivers it. See [Email & notifications](./email.md).\n\n**Why don’t notification emails arrive in production?**\nUsually no email provider is selected. Open EmDash → Settings → Email, activate\na provider (e.g. `emdash-smtp` or Cloudflare Email), send a test, then retry.\nIn local `astro dev`, EmDash logs mail to the terminal (console provider).\n\n**How are submissions protected from spam?**\nEvery form has a honeypot and signed CSRF. Optional Cloudflare Turnstile is on\nthe form’s Protection tab. With OpenAI spam scoring enabled, emails are skipped\nat/above the threshold (the submission is still saved).\n\n**Can I get notified about new submissions?**\nYes. Attach a built-in or custom email template on the form’s Notifications\ntab (submitter field or custom address), or fire signed HTTPS webhooks.\n\n**How do I export or delete someone’s data?**\nFreeform → Submissions → open a row → **Export CSV** or **Delete**. See\n[Privacy](./privacy.md).\n\n**Can AI agents manage my forms?**\nYes. The optional MCP server (`emdash-freeform-mcp`) exposes form and\nsubmission tools to clients like Claude and Cursor.\n\n**Are the rendered forms styled?**\nNo — by design. `@solspace/freeform-astro` renders semantic HTML with\n`data-freeform-*` attributes so forms match your site’s design system.\n","security":"# Security\n\n**Reporting a vulnerability**\n\nEmail **support@solspace.com**. Please do not open a public issue for\nsecurity problems.\n\n**How Freeform protects your site**\n\n- Submission forms carry a honeypot field and HMAC-signed CSRF tokens\n  (timing-safe verification, 2-hour expiry).\n- Outbound webhooks are signed with HMAC-SHA256 (`X-Freeform-Signature`)\n  so receivers can verify authenticity.\n- API keys you provide (e.g. OpenAI) are stored in your site's own\n  plugin storage — they are never sent to Solspace.\n- Outbound notification email uses EmDash’s `email:send` pipeline and whatever\n  provider the site operator configured under Settings → Email (Freeform does\n  not store SMTP passwords).\n- Export links are short-lived, signed tokens; they expire automatically.\n","description":"# Freeform for EmDash\n\nBuild forms, collect submissions, and act on them — without leaving EmDash.\n\n![Freeform Form Builder](https://raw.githubusercontent.com/solspace/emdash-freeform/main/screenshots/form-builder.jpg)\n\n### Form builder\n\n- 13 field types: text, email, textarea, number, phone, date, checkboxes, radio, dropdowns, multi-select, hidden, and HTML blocks\n- Row-based layout with per-field validation (length, pattern, min/max)\n- An AI builder that creates and edits forms from a plain-English description\n- Starter forms for contact and newsletter signup out of the box\n\n### Submissions\n\n![Freeform Forms Dashboard](https://raw.githubusercontent.com/solspace/emdash-freeform/main/screenshots/forms-dashboard.jpg)\n\n- A searchable submissions inbox with pagination and full detail views\n- AI submission briefs: intent, urgency, key facts, and a suggested next action\n- CSV export for spreadsheets and reporting\n\n### Automation & protection\n\n![Freeform Email Templates and Settings](https://raw.githubusercontent.com/solspace/emdash-freeform/main/screenshots/email-templates.jpg)\n\n- Email notifications with reusable templates (delivery via EmDash’s email provider — Settings → Email)\n- Privacy tooling: consent fields, retention auto-delete, export/delete submissions\n- Signed webhooks (HMAC-SHA256) with automatic retries and a delivery log\n- Spam scoring with honeypot + CSRF protection on every submission\n- Optional MCP server so AI agents can manage forms and read submissions\n\nForms render on your site with the companion `@solspace/freeform-astro`\npackage, which ships unstyled components you theme with your own CSS.\n","installation":"# Installation\n\n## 1. Install the plugin\n\nMake sure the registry is enabled in your `astro.config.mjs`\n(`emdash({ registry: \"https://registry.emdashcms.com\", … })`, EmDash ≥ 1.0),\nthen in the EmDash admin go to **Plugins → Registry**, find **Freeform**, and\ninstall it. Freeform appears in the sidebar once installed.\n\n## 2. Render forms on your site\n\nFreeform's admin lives in the plugin; the public forms are rendered by a\nsmall companion Astro package:\n\n```bash\npnpm add @solspace/freeform-astro\n```\n\n```ts\n// astro.config.mjs\nimport freeformAstro from \"@solspace/freeform-astro\";\n\nexport default defineConfig({\n  integrations: [emdash(), freeformAstro()],\n});\n```\n\nThen embed any form by its handle:\n\n```astro\n---\nimport { FreeformForm } from \"@solspace/freeform-astro/components\";\n---\n\n<FreeformForm formId=\"contact\" />\n```\n\n## 3. Email delivery (required for real notification mail)\n\nFreeform does **not** configure SMTP/IMAP. Configure EmDash first:\n\n1. Install an email provider plugin (e.g. `emdash-smtp` or Cloudflare Email)\n2. **EmDash admin → Settings → Email** → select provider → **Send test email**\n3. Then attach Freeform notification templates to your forms\n\nFull details: [Email & notifications](./email.md).\n\nIn local `astro dev`, EmDash uses a console provider (logs to the terminal) so\nyou can test Freeform notifications without SMTP.\n\n## 4. Optional: AI features\n\nGenerate with AI, spam scoring, and submission briefs need your own OpenAI API\nkey under **Freeform → Settings → AI & spam**. Without a key, everything else —\nbuilding forms by hand, submissions, email (via EmDash), webhooks — works\nnormally. The **Generate with AI** button only appears once a key is saved.\n"},"extensions":{"com.emdashcms.experimental.package.profileExtension":{"$type":"com.emdashcms.experimental.package.profileExtension","repository":"https://github.com/solspace/emdash-freeform","releasePolicy":{"$type":"com.emdashcms.experimental.package.profileExtension#releasePolicy","requireProvenance":false}}},"lastUpdated":"2026-11-06T18:34:12.038Z","slug":"freeform"},"indexedAt":"2026-11-06T18:34:20.684Z","labels":[{"ver":1,"src":"did:web:labels.emdashcms.com","uri":"at://did:plc:tsp7az5h6qsqjzqsgz37wonx/com.emdashcms.experimental.package.profile/freeform","val":"listing-passed","cts":"2026-11-07T10:46:41.359Z","cid":"bafyreihxjlubvbggxppcm3wtnkq33cl2ftj4gqgmln3f4jyv7supiqubhq"},{"ver":1,"src":"did:web:labels.emdashcms.com","uri":"at://did:plc:tsp7az5h6qsqjzqsgz37wonx/com.emdashcms.experimental.package.profile/freeform","val":"listing-overridden","cts":"2026-11-07T10:46:41.359Z","cid":"bafyreihxjlubvbggxppcm3wtnkq33cl2ftj4gqgmln3f4jyv7supiqubhq"},{"ver":1,"src":"did:web:labels.emdashcms.com","uri":"at://did:plc:tsp7az5h6qsqjzqsgz37wonx/com.emdashcms.experimental.package.profile/freeform","val":"listing-review","cts":"2026-11-07T10:46:41.359Z","cid":"bafyreihxjlubvbggxppcm3wtnkq33cl2ftj4gqgmln3f4jyv7supiqubhq","neg":true}],"handle":"solspace.com","latestVersion":"1.3.0"},{"uri":"at://did:plc:fk3sarba5elb3q26v7gkgz74/com.emdashcms.experimental.package.profile/agent-ready","cid":"bafyreidpcxxyzaw5utqt2nzymluhoz43uqatcdpgq233ful7hzphozc3ne","did":"did:plc:fk3sarba5elb3q26v7gkgz74","slug":"agent-ready","profile":{"$type":"com.emdashcms.experimental.package.profile","id":"at://did:plc:fk3sarba5elb3q26v7gkgz74/com.emdashcms.experimental.package.profile/agent-ready","type":"emdash-plugin","license":"Apache-2.0","authors":[{"url":"https://workpods.ai/tools/agent-ready","name":"WorkPods"}],"security":[{"url":"https://github.com/WorkPodsAgents/emdash-agent-ready/security"}],"name":"Agent Ready by WorkPods","description":"Finds what search engines, AI answers and agents trip on in your content: titles, descriptions, headings, alt text, links.","keywords":["seo","geo","axo","ai-agents","content-quality"],"sections":{"faq":"### Why does it say my body has an extra H1?\n\nEmDash templates render the entry title as the page's H1, so headings in the body should start\nat H2. If your template does not render the title as an H1, ignore this finding.\n\n### Which title and description does it check?\n\nThe ones search engines get: the SEO panel's title and description, falling back to the entry\ntitle and excerpt, the same way EmDash's `getSeoMeta()` does.\n\n### Does it check my live pages?\n\nNo. It reads content inside EmDash only, so it needs no network access. To check rendered pages,\n`llms.txt` and `robots.txt`, use the free\n[Agent Ready Chrome extension](https://workpods.ai/tools/agent-ready).\n\n### Why does it say \"Partial scan\"?\n\nEach page load reads up to 800 published entries, eight list calls of 100, because sandboxed\nplugins on Cloudflare may make only ten host calls per request.\n","changelog":"## 0.1.0\n\nFirst release: an Agent Ready admin page and dashboard widget that check published entries for\ntitle and meta description length, heading outline, image alt text, thin pages, internal links\nand duplicate titles or descriptions.\n","installation":"## Install from the registry\n\nYour site needs a sandbox runner: `sandbox()` from `@emdash-cms/cloudflare` on Cloudflare Workers\n(Workers Paid plan), or `@emdash-cms/sandbox-workerd` with `workerd` on Node.js. See\n[Plugin sandbox](https://docs.emdashcms.com/deployment/plugin-sandbox/).\n\n1. Open **Registry** in the EmDash admin and search for **Agent Ready**.\n2. Select **Install** and confirm the two permissions: read content and read the content schema.\n3. Open **Agent Ready** in the sidebar, or add the **Agent Ready** widget to the dashboard.\n\n## What it reads\n\nEvery published entry in each routable collection, except entries marked **Hide from search\nengines**. It reads titles, the SEO panel, excerpts, Portable Text bodies and image fields. It\nwrites nothing and makes no network requests.\n\n## Site URL\n\nSet **Site URL** in **Settings > General** so absolute links to your own domain count as internal\nlinks. Restart the site after changing it; plugins read the URL when they load.\n"},"extensions":{"com.emdashcms.experimental.package.profileExtension":{"$type":"com.emdashcms.experimental.package.profileExtension","repository":"https://github.com/WorkPodsAgents/emdash-agent-ready","releasePolicy":{"$type":"com.emdashcms.experimental.package.profileExtension#releasePolicy","requireProvenance":false}}},"lastUpdated":"2026-11-06T15:35:23.003Z","slug":"agent-ready"},"indexedAt":"2026-11-06T15:35:30.985Z","labels":[{"ver":1,"src":"did:web:labels.emdashcms.com","uri":"at://did:plc:fk3sarba5elb3q26v7gkgz74/com.emdashcms.experimental.package.profile/agent-ready","val":"listing-passed","cts":"2026-11-06T15:35:36.531Z","cid":"bafyreidpcxxyzaw5utqt2nzymluhoz43uqatcdpgq233ful7hzphozc3ne"}],"handle":"workpods.ai","latestVersion":"0.1.0"},{"uri":"at://did:plc:3g3mjpfnhqr4icd3ukfgyz4s/com.emdashcms.experimental.package.profile/article-contents","cid":"bafyreiftal42u7m2fqhfbefcxd2psfcxndifsgsdedxpo4onrbfqlbdfaa","did":"did:plc:3g3mjpfnhqr4icd3ukfgyz4s","slug":"article-contents","profile":{"$type":"com.emdashcms.experimental.package.profile","id":"at://did:plc:3g3mjpfnhqr4icd3ukfgyz4s/com.emdashcms.experimental.package.profile/article-contents","type":"emdash-plugin","license":"MIT","authors":[{"name":"LLL Inc."}],"security":[{"email":"contact@lx3.cafe"}],"name":"LLL Article Contents / 記事目次","description":"Free native article contents and stable section links for owner-configured Node SSR themes. No CMS article grants.","lastUpdated":"2026-11-05T14:31:00.655Z","slug":"article-contents"},"indexedAt":"2026-11-05T14:31:08.188Z","labels":[{"ver":1,"src":"did:web:labels.emdashcms.com","uri":"at://did:plc:3g3mjpfnhqr4icd3ukfgyz4s/com.emdashcms.experimental.package.profile/article-contents","val":"listing-passed","cts":"2026-11-05T14:31:14.186Z","cid":"bafyreiftal42u7m2fqhfbefcxd2psfcxndifsgsdedxpo4onrbfqlbdfaa"}],"handle":"mrhandsome39.bsky.social","latestVersion":"0.1.1"},{"uri":"at://did:plc:3g3mjpfnhqr4icd3ukfgyz4s/com.emdashcms.experimental.package.profile/campaign-url-builder","cid":"bafyreicuyvr6ls6pgoljyj3edv5sgvqjoomdmqfwxyn2ol4gewfap6ezne","did":"did:plc:3g3mjpfnhqr4icd3ukfgyz4s","slug":"campaign-url-builder","profile":{"$type":"com.emdashcms.experimental.package.profile","id":"at://did:plc:3g3mjpfnhqr4icd3ukfgyz4s/com.emdashcms.experimental.package.profile/campaign-url-builder","type":"emdash-plugin","license":"MIT","authors":[{"name":"LLL Inc.","email":"contact@lx3.cafe"}],"security":[{"email":"contact@lx3.cafe"}],"name":"LLL Campaign URL Builder","description":"Generate one campaign URL with UTM tags. English/Japanese, no saved data or external requests.","keywords":["utm","campaign","marketing","url-builder"],"sections":{"faq":"# FAQ\n\n**Does this verify Analytics or count clicks?** No. It only generates UTM tags. Visits/reporting depend on destination measurement, consent, redirects and user activity. No Google account or tracking connection.\n\n**Are URLs saved, sent or opened?** No plugin storage/logs/network/shortening/opening or clipboard access. Input/output stays in the current admin response/browser. Host/proxy/browser retention is separate. Protect copied URLs; never include secrets or personal data.\n\n**Existing UTM tags?** Any decoded utm_* key is rejected case-insensitively, including duplicates and utm_id. Remove old tags explicitly. Other query keys (including repeats) and fragments are preserved.\n\n**Unicode/IDN?** Version0.1.1 validates with bundled standard WHATWG parseURL, avoiding the legacy URL implementation in the tested Node dev runner. Original host/path/query/fragment text is retained in output. Standard IDNA data is losslessly compressed for packaging; no extra permission/network. Case and inner spaces in tags stay unchanged and are encoded. Leading/trailing whitespace and controls are rejected.\n\n**Limits and scope?** Single HTTPS URL4096UTF8B, each tag100Unicode codepoints/400B, encoded result8192B; private request16KiB. No destination safety/reachability check. Free MIT version; no paid edition, history, presets, bulk, QR or automatic download.\n\n**日本語は？** Languageで日本語を選び、結果全文を手動コピーします。生成とGA接続・受信確認は別です。\n\nSupport: contact@lx3.cafe; version and redacted synthetic steps only, never secrets/private URLs.\n","security":"# Security and privacy\n\nLLL Inc. Version0.1.1,2026-11-05. MIT. This describes the implemented boundary, not a compliance certification.\n\nNo capabilities, allowedHosts or hooks. One private POST JSON admin route,16KiB, host plugins:manage authorization. No public route, content/schema access, CMS/KV writes, HTTP, cron, Google/API credentials, telemetry or payment integration. Host-attested user identity is required; input cannot select authority.\n\nURL/tag processing is ephemeral in the current admin request and returned plain code. This plugin never logs or stores input/output. Host, proxy and browser request retention is governed separately. Do not enter personal information, credentials, secret tokens or private URLs. Copying/sharing intentionally reveals query values to recipients; do not publish confidential campaigns accidentally.\n\nOnly absolute HTTPS strings accepted; userinfo (including empty raw@), controls, backslashes, malformed percent escapes and ambiguous empty authority rejected. All pre-existing case-insensitive decoded utm_* query keys are refused. No replacement, destination fetch or opening. Output is code/text, not an active link. Limits: base4096UTF8B, fields100codepoints/400B each, output8192B. No real-world destination safety or Analytics-receipt guarantee.\n\nReport vulnerabilities to contact@lx3.cafe with version and a redacted synthetic reproduction. No SLA/bounty offered. Do not send live tokens, personal data, account access or private URLs.\n\nURL validation uses pinned whatwg-url14.2.0 parseURL with unchanged standard IDNA algorithms. Standard tr465.1.1 table and RegExp source/flags are losslessly compressed in the build and restored from fixed bundled bytes; no user data is decompressed. tiny-inflate1.0.3 only restores those constants. No global URL mutation, custom host parser, shared runner setting change, new capability or connection. Full dependency licenses are included in README.\n","changelog":"# Changelog\n\n## 0.1.2 — 2026-11-05\n\nPackaging-only maintenance: include the complete original EmDash1.4.0 runtime MIT copyright and license in the distributed README. Backend behavior, dependencies, limits and permissions remain identical to0.1.1. Old0.1.1 artifacts remain unchanged history.\n\n## 0.1.1 — 2026-11-05\n\nFixes IDN/host validation in the official Node development sandbox by using a self-contained standard WHATWG parseURL implementation. Standard IDNA data is losslessly compressed for official size limits. Preserves original URL text in output and prior UTM/authority/field/byte rejection rules. No capability, network, storage or UI workflow change;0.1.0 release/artifact remains unchanged.\n\n## 0.1.0 — 2026-11-05\n\nInitial single-URL campaign tag generator. English/Japanese private administration, source/medium/campaign plus optional content/term, exact non-UTM query/fragment retention and manual code copy. Existing UTM parameters rejected. No saved state, requested capabilities, external requests, tracking, shortening, account connection or automatic opening.\n","installation":"# Installation and use\n\nVersion0.1.1. Tested with EmDash1.4.0. Administrator/plugin-management access is required.\n\nFind LLL Campaign URL Builder in the standard EmDash Registry after public availability. Publisher: LLL Inc. (mrhandsome39.bsky.social). Check the signed release; this product requests no capabilities and no allowed network hosts. Open Campaign URL Builder in administration.\n\nPaste an absolute HTTPS destination URL. Enter source, medium and campaign; content/term are optional. Choose English or Japanese and Generate. Select the entire plain code result and copy manually. The result is not automatically opened or posted. This version rejects all existing utm_* query keys, including uppercase or repeated ones. Explicitly remove them yourself before rebuilding. Other query parameters and the fragment are retained; tag case and inner spaces are preserved.\n\nKeep personal information, passwords and secret tokens out of both URL and tags. Do not include private URLs. Generating a URL does not connect Analytics, prove a visit or verify receipt. Configure measurement and consent independently at the destination and test reception there. No analytics identifier or Google account is requested here.\n\n入力は絶対HTTPS URL、source/medium/campaign必須、content/term任意です。既存utm_*は自動上書きしません。日本語に切り替えて生成し、コード全文を手動コピーしてください。GA受信やリンク先の到達は確認しません。\n"},"lastUpdated":"2026-11-05T12:26:31.421Z","slug":"campaign-url-builder"},"indexedAt":"2026-11-05T12:28:30.295Z","labels":[{"ver":1,"src":"did:web:labels.emdashcms.com","uri":"at://did:plc:3g3mjpfnhqr4icd3ukfgyz4s/com.emdashcms.experimental.package.profile/campaign-url-builder","val":"listing-passed","cts":"2026-11-05T12:28:33.084Z","cid":"bafyreicuyvr6ls6pgoljyj3edv5sgvqjoomdmqfwxyn2ol4gewfap6ezne"}],"handle":"mrhandsome39.bsky.social","latestVersion":"0.1.2"},{"uri":"at://did:plc:3g3mjpfnhqr4icd3ukfgyz4s/com.emdashcms.experimental.package.profile/migration-reconciler","cid":"bafyreietbbeol7t72nxarznr3pzvyen6oovge4p572fuwppikw64nobpca","did":"did:plc:3g3mjpfnhqr4icd3ukfgyz4s","slug":"migration-reconciler","profile":{"$type":"com.emdashcms.experimental.package.profile","id":"at://did:plc:3g3mjpfnhqr4icd3ukfgyz4s/com.emdashcms.experimental.package.profile/migration-reconciler","type":"emdash-plugin","license":"MIT","authors":[{"name":"LLL Inc.","email":"contact@lx3.cafe"}],"security":[{"email":"contact@lx3.cafe"}],"name":"LLL CSV Migration Reconciler","description":"Compare a pasted CSV with saved CMS content by exact key and locale. Read-only, bilingual, with duplicate checks and bounded CSV reports.","keywords":["csv","migration","reconciliation","content-audit","read-only"],"sections":{"faq":"# FAQ\n\nVersion 0.1.1.\n\n**What is free?** Paste CSV, compare up to three text fields, scan manually and copy report CSV. No API key/paid contract; no Pro/add-ons are sold. Host fees are separate. No import, repair, backup or rollback.\n\n**How do I start?** One form holds CSV, exact key/comparison header names, CMS field selections and collection/status/locale scope. Press Start to parse and validate together. No separate parse/mapping step or CSV echo. Invalid input is wholly rejected.\n\n**How does matching work?** Exact key+locale and text: 001 differs from1; case/spaces/newlines matter. No numeric/date/URL equivalence. Null/absent differs from empty; other non-string values are unsupported. Duplicates on either side are conflicts. Choose CSV locale column or one fixed locale for all rows/CMS filter. Published is default; draft needs deliberate selection. Public URLs are unnecessary.\n\n**Is missing final?** During scanning it is pending. Missing is confirmed only after all selected scope is read without unsupported items/failure/scan cap. It is not site-wide absence or a fixed snapshot. Output truncation is separate; a complete scan may export only some findings.\n\n**Limits and privacy?** CSV32KiB/100rows/32cols/4KiB cell; scan5/batch,1,000 total,128KiB selected JSON/record. Report200 findings/final formula-safe CSV48KiB; state512KiB. Copy displayed CSV and save UTF-8 manually; no download. Formula protection means it is not a lossless backup. No raw CSV/full parsed table/unselected columns stored: only selected keys/locales/values/row numbers. One report/user expires logically24h; next read/Clear deletes state, no background cleanup. No HTTP/CMS writes. Protect copied files. Contact:contact@lx3.cafe.\n\n日本語:1フォームでStartまで設定。rawCSVや全表は保存せず選択値だけ保存。24hは論理期限。無料、Pro未販売。\n","security":"# Security and data\n\nVersion 0.1.1 · LLL Inc. · 2026-11-05. These are the data-handling boundaries, not a compliance certification.\n\nOnly content:read/schema:read; private admin access requires plugins:manage. No public report route, CMS/SEO/redirect writes, import/apply, publish, network calls, email, telemetry, cron, external keys or paid API. No public URL or HTTP reachability checks.\n\nCSV is pasted and parsed in the installation. Raw CSV text and unselected columns are not persisted in plugin state. Selected keys, locales, comparison values, source row numbers and scan/report metadata are stored; these remain potentially confidential source data. Explicit draft scope may expose draft values to the authorized operator. Do not paste credentials/tokens.\n\nOne report per authenticated user logically expires24h after Start. State is deleted on next read after expiry or manual Clear; no background physical-cleanup guarantee. Plugin controls cannot erase browser history, clipboard or files saved/shared by the user. Protect copied CSV yourself.\n\nCaps:input32KiB/100rows/32cols/4KiB cell; key/locale256/64bytes; three comparison fields; scan1,000records/128KiB selected JSON per record; state512KiB. Output200 non-equal findings/final quoted, formula-protected CSV48KiB. Preview text is shortened. Malformed input is rejected as a whole. Data is rendered as text; output quotes cells and protects potential formulas with an apostrophe. This report is not a lossless backup or automatic reimport source.\n\nReport vulnerabilities privately to contact@lx3.cafe with product/version, expected/observed behavior and minimal redacted steps. Do not send full confidential CSV, tokens, passwords or authentication state. Agree a secure channel before sensitive evidence. No response-time commitment or bug bounty is offered. Full MIT notice is in README/LICENSE.\n\n日本語:選択値も機密情報です。24hは論理期限で、背景の物理削除を保証しません。\n","changelog":"# Changelog\n\n## 0.1.2 — 2026-11-05\n\nPackaging-only maintenance: include the complete original EmDash1.4.0 runtime MIT copyright and license in the distributed README. Backend behavior, dependencies, limits and permissions remain identical to0.1.1. Old0.1.1 artifacts remain unchanged history.\n\n## 0.1.1\n\n2026-11-05. Fix Previous/Next controls when moving between report pages. All read-only comparison/audit behavior and capabilities remain unchanged.\n\n## 0.1.0\n\nInitial version, 2026-11-05.\n\n- Bounded CSV paste and read-only key+locale reconciliation against one EmDash collection/status scope.\n- Up to three exact text comparisons, duplicate conflicts, provisional pending and scope-limited missing results.\n- Manual batches, separate scan/output limits, CSV text for manual copy/save.\n- English/Japanese admin UI; selected values only in user-scoped report state, logical24h expiry.\n- Free workflow; no Pro edition or paid add-on is currently sold.\n","installation":"# Installation and workflow\n\nVersion 0.1.1.\n\nTarget: EmDash 1.4.0 with the official sandbox runner (Node/workerd or supported Cloudflare paid Worker Loader). Hosting fees are separate. Find LLL CSV Migration Reconciler / migration-reconciler in Registry. Check LLL publisher, signature and only content:read/schema:read; install as admin.\n\nUse one form: paste CSV, enter exact CSV key/comparison header names, select CMS fields, collection/status/locales and locale mode. Published is default; draft data may be confidential. Choose a CSV locale column or one fixed locale for all rows (also the CMS filter). Compare up to three supported top-level text fields.\n\nPress Start to parse and validate mappings together; errors reject the whole input. No separate parse/mapping step, full parsed-table storage or raw CSV echo. Only selected keys/locales/values/row numbers enter report state. Continue in five-record batches. No import/repair. Public URLs are unnecessary.\n\nUnmatched is pending until the entire selected scope is read without unsupported items/failure/scan cap. Duplicates are conflicts. Export truncation differs from incomplete scanning.\n\nCaps: input32KiB/100rows/32cols/4KiB cell; key/locale256/64bytes; scan1,000records/128KiB selected JSON per record; output200 findings/final formula-safe CSV48KiB; state512KiB. Show CSV, copy all text, save UTF-8 manually; no download.\n\nOne report/user expires logically24h after Start; next read/Clear deletes state, no background cleanup. Protect copied files. Free comparison/CSV; Pro is not sold. Contact:contact@lx3.cafe.\n\n日本語:1フォームにCSVと厳密な列名・CMS項目・範囲・localeを入力しStart。別解析画面なし、rawCSV再表示/全表保存なし。無料、Pro未販売。\n"},"lastUpdated":"2026-11-05T12:18:02.648Z","slug":"migration-reconciler"},"indexedAt":"2026-11-05T12:22:53.304Z","labels":[{"ver":1,"src":"did:web:labels.emdashcms.com","uri":"at://did:plc:3g3mjpfnhqr4icd3ukfgyz4s/com.emdashcms.experimental.package.profile/migration-reconciler","val":"listing-passed","cts":"2026-11-05T12:22:58.315Z","cid":"bafyreietbbeol7t72nxarznr3pzvyen6oovge4p572fuwppikw64nobpca"}],"handle":"mrhandsome39.bsky.social","latestVersion":"0.1.2"},{"uri":"at://did:plc:3g3mjpfnhqr4icd3ukfgyz4s/com.emdashcms.experimental.package.profile/public-csv-table","cid":"bafyreibljnopo7pywbtg2vgbagq6wxtmsdnwqaroflfjtdzsyly3fq3e5m","did":"did:plc:3g3mjpfnhqr4icd3ukfgyz4s","slug":"public-csv-table","profile":{"$type":"com.emdashcms.experimental.package.profile","id":"at://did:plc:3g3mjpfnhqr4icd3ukfgyz4s/com.emdashcms.experimental.package.profile/public-csv-table","type":"emdash-plugin","license":"MIT","authors":[{"name":"LLL Inc."}],"security":[{"email":"contact@lx3.cafe"}],"name":"LLL Public CSV Table","description":"Privately review a complete CSV, explicitly publish one table, and render searchable full HTML using the included Node SSR kit.","lastUpdated":"2026-11-05T11:57:16.646Z","slug":"public-csv-table"},"indexedAt":"2026-11-05T12:00:49.868Z","labels":[{"ver":1,"src":"did:web:labels.emdashcms.com","uri":"at://did:plc:3g3mjpfnhqr4icd3ukfgyz4s/com.emdashcms.experimental.package.profile/public-csv-table","val":"listing-passed","cts":"2026-11-05T12:01:11.163Z","cid":"bafyreibljnopo7pywbtg2vgbagq6wxtmsdnwqaroflfjtdzsyly3fq3e5m"}],"handle":"mrhandsome39.bsky.social","latestVersion":"0.1.0"},{"uri":"at://did:plc:6clqzka76ueshbureaecbrfj/com.emdashcms.experimental.package.profile/quixseo","cid":"bafyreibgs6oc53dg5cqbjyojctrfg4k3fugoo37qjebowwjfadjjcoja34","did":"did:plc:6clqzka76ueshbureaecbrfj","slug":"quixseo","profile":{"$type":"com.emdashcms.experimental.package.profile","id":"at://did:plc:6clqzka76ueshbureaecbrfj/com.emdashcms.experimental.package.profile/quixseo","type":"emdash-plugin","license":"MIT","authors":[{"url":"https://emquix.com","name":"EmQuix"}],"security":[{"url":"https://github.com/emquix-com/QuixSEO/security"}],"name":"QuixSEO","description":"Complete SEO Toolkit for EmDash · EmQuix — editorial guidance, structured data and reviewed site management.","keywords":["seo","metadata","schema","audit","migration"],"sections":{"faq":"### Is QuixSEO free?\n\nYes. There are no paid features, advertising or required plugin accounts. Hosting costs are separate.\n\n### Where do I edit my SEO title and description?\n\nUse EmDash's native SEO panel. QuixSEO analyzes those values; native SEO bulk edits and migration writes are preview-only on the tested bridge.\n\n### Why is there a separate save button?\n\nUse **Save QuixSEO settings** for keyphrases, cornerstone, additional robots flags, social overrides and schema. The ordinary EmDash Save does not save pending QuixSEO settings.\n\n### Can it analyze unpublished changes?\n\nYes. The saved-entry panel identifies saved unpublished changes. The explicit current-editor analysis checks an unsaved snapshot without publishing it. Public metadata does not use unpublished body changes.\n\n### Does it send content to AI or track visitors?\n\nNo. QuixSEO has no external AI calls, outbound network requests or visitor analytics tracker.\n\n### Does a high score guarantee rankings?\n\nNo. Scores summarize the checks that could run. Review coverage and unavailable checks; schema does not guarantee rich results.\n\n### Does it replace sitemaps or robots.txt?\n\nNo. EmDash owns native sitemaps and indexing controls. The optional llms.txt output needs a host route and does not modify robots.txt.\n\n### Can I undo changes?\n\nEligible plugin-owned bulk/import changes support conditional rollback while current values still match the change record. Native fields and redirects are outside that journal. Back up before migration.\n\n### Can I run another SEO plugin?\n\nUse one owner for overlapping metadata and schema. Analysis can run with QuixSEO's output disabled. Full compatibility with other SEO plugins is not verified.\n","security":"## Permissions and data\n\nQuixSEO runs in EmDash's plugin sandbox. It requests content and revision reads, schema reads, explicit current-editor draft reads, and redirect read/write access for its documented features.\n\nIt does not request outbound network access and makes no external AI calls. Entry settings, audit reports and change history are stored in plugin-scoped storage on your site. Reports may contain content and metadata; restrict administrator/editor access appropriately. Automatic retention cleanup is not available yet.\n\nThe optional public llms.txt endpoint reads eligible published content and excludes drafts and noindex entries according to the documented rules. Enable it only when you want that public output.\n\nPlugin-owned changes use validation and conflict checks; supported rollback checks current values before restoring them. Native SEO writes are blocked on the tested bridge. Redirects are not covered by the change-history rollback journal.\n\n## Report a vulnerability\n\nFollow the repository's security policy. Use private vulnerability reporting where available and never post credentials, private exports or sensitive site data in public issues.\n\n[Security policy and reporting](https://github.com/emquix-com/QuixSEO/security)\n\nQuixSEO is MIT-licensed and supplied without warranty. Independent security certification and full compatibility testing with other plugins have not been completed.\n","changelog":"## 0.0.2 — 5 November 2026\n\n- Updated the public author identity to EmQuix and emquix.com.\n- Added the EmQuix SEO icon with the shared plugin-family label style.\n- No plugin runtime or permission changes from 0.0.1.\n- Packaged Linux production browser workflows and restart checks passed.\n\n## 0.0.1 — 4 November 2026\n\n- First published release: content analysis, search previews and focus keyphrases.\n- Separate saves for plugin-owned SEO settings, social overrides and schema, with validation and stale-form protection.\n- Published-content audits, duplicate metadata findings and internal-link guidance.\n- Reviewed plugin-owned bulk changes, mapped migration fixtures and conditional rollback; native SEO changes remain preview-only.\n- Exact-path redirect management and optional llms.txt generation.\n\n[Full changelog](https://github.com/emquix-com/QuixSEO/blob/main/CHANGELOG.md)\n","description":"## Complete SEO Toolkit for EmDash\n\nQuixSEO by EmQuix helps you review content, improve search appearance and manage technical SEO from your EmDash admin. Free and open source under the MIT license, with no plugin account, paid features, ads or external AI calls.\n\n### Plan, review and improve\n\n- Analyze saved content and explicitly check unsaved editor changes, with SEO and readability guidance, focus keyphrases and search previews.\n- Save cornerstone flags, additional robots directives, Open Graph and X overrides, and validated structured data.\n- Run published-content audits with automatic progress when the host scheduler is configured. Review duplicate SEO metadata, link counts and potential orphaned pages.\n- Find related internal-link opportunities, review plugin-owned bulk changes and roll back eligible changes.\n- Manage exact-path redirects and preview mapped Yoast/Rank Math imports.\n- Optionally generate llms.txt content, with preview, download and a documented host route.\n\n### Built around native EmDash SEO\n\nEmDash owns SEO titles, descriptions, canonicals, the main social image and noindex. Edit those in its native SEO panel; QuixSEO reads them for analysis. Native SEO bulk writes and imports are preview-only on the tested sandbox bridge.\n\nScores are editorial guidance, not ranking predictions. Link analysis covers published content; it does not crawl navigation menus or external backlinks. The optional llms.txt feature does not guarantee AI discovery or citations.\n\n[Read the documentation](https://github.com/emquix-com/QuixSEO#documentation)\n","installation":"## Requirements\n\nThe tested production setup is Linux, Node.js 24, EmDash 1.4.0 and the official Node/workerd sandbox. A packaged 0.0.2 build passed production browser workflows and restart checks. Fresh installation from the public registry remains a separate verification step.\n\nWindows development was tested; Windows standalone production and deployed Cloudflare are not verified targets. Cloudflare sandboxed plugins require Workers Paid.\n\n## Install and set up\n\n1. Back up your existing site and confirm its plugin sandbox is configured.\n2. Open Registry in EmDash, find **QuixSEO** from **@emquix.com**, and select version **0.0.2**. Review the permissions and confirm installation.\n3. Enable native SEO and routability for the content types you use. Configure your public Site URL in EmDash Settings.\n4. Open **QuixSEO → Setup & settings**, then open a post or page and its QuixSEO panel.\n5. Add a focus keyphrase and choose **Save QuixSEO settings**. Save native SEO values through EmDash's own controls.\n6. Start a site review from **QuixSEO → Overview**. Automatic processing requires scheduled tasks on the host.\n\nYour theme must use EmDash's public SEO integration to render metadata. Installing a plugin cannot repair a theme that omits those hooks.\n\nThe optional llms.txt generator is disabled by default. Serving it at `/llms.txt` needs a host route; robots.txt changes are manual.\n\n[Host integration and installation details](https://github.com/emquix-com/QuixSEO/blob/main/docs/installation.md)\n"},"extensions":{"com.emdashcms.experimental.package.profileExtension":{"$type":"com.emdashcms.experimental.package.profileExtension","repository":"https://github.com/emquix-com/QuixSEO","releasePolicy":{"$type":"com.emdashcms.experimental.package.profileExtension#releasePolicy","requireProvenance":false}}},"lastUpdated":"2026-11-05T11:50:19.559Z","slug":"quixseo"},"indexedAt":"2026-11-05T11:50:27.087Z","labels":[{"ver":1,"src":"did:web:labels.emdashcms.com","uri":"at://did:plc:6clqzka76ueshbureaecbrfj/com.emdashcms.experimental.package.profile/quixseo","val":"listing-review","cts":"2026-11-04T13:13:37.714Z","cid":"bafyreiecwvbmvxcpmhp3ljw63icgzwzw6zixxipz7s26r2rsiz4nyhb7oi"},{"ver":1,"src":"did:web:labels.emdashcms.com","uri":"at://did:plc:6clqzka76ueshbureaecbrfj/com.emdashcms.experimental.package.profile/quixseo","val":"listing-passed","cts":"2026-11-05T11:50:30.437Z","cid":"bafyreibgs6oc53dg5cqbjyojctrfg4k3fugoo37qjebowwjfadjjcoja34"}],"handle":"emquix.com","latestVersion":"0.0.2"},{"uri":"at://did:plc:myrtyr2rsjiivzlp5m6qevv5/com.emdashcms.experimental.package.profile/auto-thumbnail","cid":"bafyreie6zq22edqjqealmpm662m3fff2oxbrw5hjnis3hggfg3zpn2e42q","did":"did:plc:myrtyr2rsjiivzlp5m6qevv5","slug":"auto-thumbnail","profile":{"$type":"com.emdashcms.experimental.package.profile","id":"at://did:plc:myrtyr2rsjiivzlp5m6qevv5/com.emdashcms.experimental.package.profile/auto-thumbnail","type":"emdash-plugin","license":"MIT","authors":[{"url":"https://bsky.app/profile/rietamura.com","name":"Rie Tamura"}],"security":[{"url":"https://github.com/RieTamura/Auto-Thumbnail/issues"}],"name":"Auto Thumbnail Design","description":"Thumbnail design for posts without an image. Needs the Obsidian plugin Post To EmDash, which draws the title and date on it.","keywords":["thumbnail","featured-image","og-image","design","obsidian"],"extensions":{"com.emdashcms.experimental.package.profileExtension":{"$type":"com.emdashcms.experimental.package.profileExtension","repository":"https://github.com/RieTamura/Auto-Thumbnail","releasePolicy":{"$type":"com.emdashcms.experimental.package.profileExtension#releasePolicy","requireProvenance":false}}},"lastUpdated":"2026-11-05T11:14:28.436Z","slug":"auto-thumbnail"},"indexedAt":"2026-11-05T11:14:36.535Z","labels":[{"ver":1,"src":"did:web:labels.emdashcms.com","uri":"at://did:plc:myrtyr2rsjiivzlp5m6qevv5/com.emdashcms.experimental.package.profile/auto-thumbnail","val":"listing-review","cts":"2026-10-30T08:51:19.123Z","cid":"bafyreiawpx3ybspccqxgf25vw4bzjekotut5j7wi4zitusyv4c42wrrgey"},{"ver":1,"src":"did:web:labels.emdashcms.com","uri":"at://did:plc:myrtyr2rsjiivzlp5m6qevv5/com.emdashcms.experimental.package.profile/auto-thumbnail","val":"listing-passed","cts":"2026-11-05T11:14:41.238Z","cid":"bafyreie6zq22edqjqealmpm662m3fff2oxbrw5hjnis3hggfg3zpn2e42q"}],"handle":"rietamura.com","latestVersion":"0.2.2"},{"uri":"at://did:plc:myrtyr2rsjiivzlp5m6qevv5/com.emdashcms.experimental.package.profile/bluesky-share","cid":"bafyreidygbj4o7uyejkwzlcc42mhmovwji22brvke4pug7hkchu3a3qypq","did":"did:plc:myrtyr2rsjiivzlp5m6qevv5","slug":"bluesky-share","profile":{"$type":"com.emdashcms.experimental.package.profile","id":"at://did:plc:myrtyr2rsjiivzlp5m6qevv5/com.emdashcms.experimental.package.profile/bluesky-share","type":"emdash-plugin","license":"MIT","authors":[{"url":"https://bsky.app/profile/rietamura.com","name":"Rie Tamura"}],"security":[{"url":"https://github.com/RieTamura/EmDash-To-Bluesky/issues"}],"name":"EmDash To Bluesky","description":"Shares a post on Bluesky once when it is first published, with its title, link and featured image. It connects only to Bluesky.","keywords":["bluesky","atproto","social","share","crosspost"],"extensions":{"com.emdashcms.experimental.package.profileExtension":{"$type":"com.emdashcms.experimental.package.profileExtension","repository":"https://github.com/RieTamura/EmDash-To-Bluesky","releasePolicy":{"$type":"com.emdashcms.experimental.package.profileExtension#releasePolicy","requireProvenance":false}}},"lastUpdated":"2026-11-05T11:01:36.858Z","slug":"bluesky-share"},"indexedAt":"2026-11-05T11:01:45.566Z","labels":[{"ver":1,"src":"did:web:labels.emdashcms.com","uri":"at://did:plc:myrtyr2rsjiivzlp5m6qevv5/com.emdashcms.experimental.package.profile/bluesky-share","val":"listing-passed","cts":"2026-11-05T11:01:50.618Z","cid":"bafyreidygbj4o7uyejkwzlcc42mhmovwji22brvke4pug7hkchu3a3qypq"}],"handle":"rietamura.com","latestVersion":"0.1.0"},{"uri":"at://did:plc:cus3srdqglppyowqrw3hvjfj/com.emdashcms.experimental.package.profile/lead-delivery","cid":"bafyreidox2ltia25tp76dhighcoxhu7ntkhvivupqxwz4ezmseg37nbn2m","did":"did:plc:cus3srdqglppyowqrw3hvjfj","slug":"lead-delivery","profile":{"$type":"com.emdashcms.experimental.package.profile","id":"at://did:plc:cus3srdqglppyowqrw3hvjfj/com.emdashcms.experimental.package.profile/lead-delivery","type":"emdash-plugin","license":"MIT","authors":[{"url":"https://moorage.dev","name":"Moorage"}],"security":[{"email":"security@moorage.dev"}],"name":"Lead Delivery: HubSpot, Zapier, Webhooks & Email","description":"Send EmDash form enquiries to HubSpot, Zapier, Make, n8n or email, with a delivery log, retries and an alert when one fails.","keywords":["hubspot","zapier","webhooks","form notifications","leads"],"sections":{"faq":"## How do I send EmDash form submissions to HubSpot?\n\nInstall this plugin, open Lead delivery, press New delivery, choose your form and HubSpot, and paste the HubSpot form's share link or embed code. Each enquiry becomes a HubSpot form submission. The HubSpot form must have the fields you send, at least Email, and CAPTCHA must be off on it.\n\n## How do I connect EmDash forms to Zapier, Make or n8n?\n\nIn New delivery choose \"Another app\" and paste the address the app gave you: in Zapier \"Webhooks by Zapier\" (a paid Zapier plan), in Make a Custom webhook, in n8n a Webhook node. Each request is signed, so the app can check it came from your site.\n\n## Does connecting a form need a web person?\n\nNot where the site can call its own address (on Cloudflare: the compatibility flag `global_fetch_strictly_public`): an admin connects it in one step by typing a code on EmDash's approval page. Lead Delivery uses that access once and throws it away. Elsewhere EmDash's form editor has no box for it yet, so someone who can call the site's API sets it once per form; New delivery shows the instructions for that form and can email them.\n\n## What if HubSpot or Zapier is down?\n\nThe enquiry is tried again 8 times over about two days. If it still does not arrive, the people on the alert list get one email, and you can press Send again.\n\n## Does it lose enquiries?\n\nNot from the moment an enquiry reaches it: each is stored once and kept until all its deliveries are done. Every enquiry is also in Forms → Submissions. If a form stops sending altogether, the quiet-form alert tells you after 3 days.\n\n## Can my clients see it?\n\nEditors see the log and can press Send again. Only admins add or change deliveries.\n\n## Is it free?\n\nYes, on any number of sites, with no account and no key. It is open source under the MIT License.\n","security":"## Reporting a problem\n\nEmail **security@moorage.dev**, not a public issue. Say which version you use, what you found and how to reproduce it. Fixes go into the latest release; update to it to receive them. The source is public on GitHub.\n\n## What it keeps and sends\n\nEach enquiry is stored in the site's own database until all its deliveries are done, then its contents are deleted; log rows are kept for 30 days. Enquiries go only to the addresses, HubSpot forms and apps you enter. Each app delivery is signed with its own secret. Connecting a form in one step uses the admin access you approve once and then throws it away; it is never stored.\n","description":"Every enquiry sent through your EmDash forms goes where you work: your inbox, HubSpot, or another app such as Zapier, Make or n8n. Each one is logged, tried again if it does not get through, and someone hears about it when it needs fixing. No account and no key.\n\n## What it does\n\n- **Deliveries**: a form and where its enquiries go, named for you (\"Contact form → Shop inbox\"). A form can have several.\n- **Email** to one or more addresses, with the form's own field labels. Reply answers the person.\n- **HubSpot**: each enquiry becomes a HubSpot form submission. Paste the form's share link or embed code; no key is stored.\n- **Another app (Zapier, Make, n8n or your own)**: flat JSON, signed with Standard Webhooks headers.\n- **A delivery log** your clients can read, with Send again.\n- **Retries**: 8 tries over about two days.\n- **Alerts** by email, at most once a day for each kind: an enquiry that could not be delivered, a form gone quiet for 3 days, retries not running.\n- **Marks, not filters**: possibly fake addresses, the same address again and possibly spam are marked at the top of each email and still sent. Each delivery can hold repeats (24 hours or 7 days) and possibly spam.\n\n## What it does not do\n\n- No Mailchimp yet.\n- File attachments are not sent; they stay in Forms → Submissions.\n- It does not replace the Forms inbox; every enquiry stays there.\n\n## Connecting a form\n\nWhere the site can call its own address, an admin connects a form in one step: type the code shown on EmDash's approval page, press Authorize, then Done, connect. The access is used once and thrown away. Elsewhere, someone who looks after the site's code connects each form once; New delivery shows the instructions and can email them.\n\n## Permissions\n\nSend web requests to the addresses you enter, and to the site itself (to connect a form in one step, with the access you approve). Send email (Email deliveries and alerts). Read the forms' notification emails to its private address, and cancel those.\n","installation":"Install from **Plugins** in your EmDash admin (it runs sandboxed there; use 0.4.0 or later), or pin it in code, in `plugins` or, from 0.4.0, in `sandboxed` (on Cloudflare a sandboxed plugin may make only 10 calls to the site per request; from 0.4.0 Lead Delivery stays within that, and up to 0.3.1 it belongs in `plugins`):\n\n```sh\nnpm install @moorage/plugin-lead-delivery\n```\n\n```js\n// astro.config.mjs\nimport { formsPlugin } from \"@emdash-cms/plugin-forms\";\nimport leadDelivery from \"@moorage/plugin-lead-delivery\";\n\nemdash({ plugins: [formsPlugin(), leadDelivery] });\n```\n\nIt needs the official forms plugin (`@emdash-cms/plugin-forms`), 0.2.11 or later: earlier versions save a ticked consent box as unticked. Then open **Lead delivery** under Marketing in the admin sidebar and press New delivery: choose a form and where its enquiries go, and a test is sent.\n"},"extensions":{"com.emdashcms.experimental.package.profileExtension":{"$type":"com.emdashcms.experimental.package.profileExtension","repository":"https://github.com/moorage-dev/moorage","releasePolicy":{"$type":"com.emdashcms.experimental.package.profileExtension#releasePolicy","requireProvenance":false}}},"lastUpdated":"2026-11-05T10:18:01.084Z","slug":"lead-delivery"},"indexedAt":"2026-11-05T10:20:55.786Z","labels":[{"ver":1,"src":"did:web:labels.emdashcms.com","uri":"at://did:plc:cus3srdqglppyowqrw3hvjfj/com.emdashcms.experimental.package.profile/lead-delivery","val":"listing-passed","cts":"2026-11-05T10:21:09.948Z","cid":"bafyreidox2ltia25tp76dhighcoxhu7ntkhvivupqxwz4ezmseg37nbn2m"}],"handle":"moorage.dev","latestVersion":"0.5.0"},{"uri":"at://did:plc:cus3srdqglppyowqrw3hvjfj/com.emdashcms.experimental.package.profile/approvals","cid":"bafyreifcccs2275g2vseefk4l22oqp7fwxhriqvzooueovmfrp35jd4wu4","did":"did:plc:cus3srdqglppyowqrw3hvjfj","slug":"approvals","profile":{"$type":"com.emdashcms.experimental.package.profile","id":"at://did:plc:cus3srdqglppyowqrw3hvjfj/com.emdashcms.experimental.package.profile/approvals","type":"emdash-plugin","license":"MIT","authors":[{"url":"https://moorage.dev","name":"Moorage"}],"security":[{"email":"security@moorage.dev"}],"name":"Content Approval","description":"Content approval for EmDash: Publish sends a page for approval; approve and publish in one step, or send it back with a note.","keywords":["content approval","editorial workflow","publish approval","review","sign off"],"sections":{"faq":"## How do I make EmDash ask for approval before publishing?\n\nInstall this plugin, open **Approvals > Settings**, tick the content types that need approval and press **Save**. From then on, pressing Publish sends the page for approval.\n\n## Who approves?\n\nChoose \"Others need my OK before publishing\" and site admins approve everyone else's changes. Choose \"I need someone else's OK before publishing\" and everyone needs approval, admins too; an editor or another admin approves. Nobody approves their own request.\n\n## Why does the editor say \"Failed to publish\"?\n\nThat title is EmDash's own and a plugin cannot change it. The text under it starts \"Sent for approval. Not live yet.\": nothing failed, the page is waiting for approval.\n\n## Can the approver publish straight away?\n\nYes. **Approve and publish** approves the exact version they looked at and puts it live in one step.\n\n## Can I see what was approved later?\n\nYes. The History tab shows, for each page decided recently, the text of its latest approval and who approved it. The page's own Revisions panel keeps every version.\n\n## What if the approver spots a typo?\n\nThey edit and save the page like anyone else, then approve. The history records that the approver edited it.\n\n## Does it work without email?\n\nYes. Requests show on the dashboard and on the Approvals page. With email set up, approvers are emailed when someone asks.\n\n## What does approval not cover?\n\nThings EmDash saves straight to the live page: the SEO panel, categories, tags and bylines of a page already live, and content types that keep no drafts. The Settings tab lists them for your site.\n\n## Is it free?\n\nYes, on any number of sites, with no account and no key. It is open source under the MIT License.\n","security":"## Reporting a problem\n\nEmail **security@moorage.dev**, not a public issue. Say which version you use, what you found and how to reproduce it. Fixes go into the latest release; update to it to receive them. The source is public on GitHub.\n\n## What it keeps and sends\n\nIt keeps requests, decisions with their notes and the text of each page's latest approval in the site's own database. It reads users' names and email addresses only to address approvers. Email goes by the site's own email service; nothing is sent to any outside service.\n","description":"A second person OKs a page before it goes live. Writers keep pressing Publish; when a page needs approval, the publish is held and becomes a request. No account and no key.\n\n## What it does\n\n- **Publish asks for approval.** On the content types you choose, Publish and Schedule are held and the writer reads \"Sent for approval. Not live yet.\" Contributors ask from the Approvals page, with an **Ask for approval** button on each of their drafts.\n- **Approve and publish in one step**, approve to publish later, or **send it back** with a note.\n- **What changed, in words**: `Content: \"£3.20\" becomes \"£3.40\"`, compared with the live page, and after an approval with the approved version.\n- **A text copy of what was approved**: the History tab shows the text of each page's latest approval, so nobody can say they never saw it.\n- **Approvers can fix a typo**: edit, save, approve. The history says it was edited by the approver.\n- **Two plain choices**: \"Others need my OK before publishing\" or \"I need someone else's OK before publishing\". Nobody approves their own request.\n- **AI tools connected to your site always need a person's OK**, on every content type.\n- **An Approvals page and a dashboard widget**: what is waiting, what was sent back, every decision.\n- **Email**, when your site can send it: to approvers when someone asks, to the writer when they decide, one reminder after two days.\n\n## What it does not do\n\n- It cannot hold what EmDash saves straight to a live page: the SEO panel, categories, tags and bylines of a page already live, and every save in a content type without drafts. The Settings tab lists them.\n- No discussion threads, chains of approvers or approval by several people.\n\n## Permissions\n\nHolds publishing (content policy); reads content, drafts and the content schema to show what changed; publishes when an approver presses Approve and publish; reads users' names and emails to address approvers; sends email.\n","installation":"Install from **Plugins** in your EmDash admin, or pin it in code:\n\n```sh\nnpm install @moorage/plugin-approvals\n```\n\n```js\n// astro.config.mjs\nimport approvals from \"@moorage/plugin-approvals\";\n\nemdash({ sandboxed: [approvals] });\n```\n\nThen open **Approvals** in the admin sidebar, tick the content that needs approval and press **Save**. Nothing is held until you do.\n"},"extensions":{"com.emdashcms.experimental.package.profileExtension":{"$type":"com.emdashcms.experimental.package.profileExtension","repository":"https://github.com/moorage-dev/moorage","releasePolicy":{"$type":"com.emdashcms.experimental.package.profileExtension#releasePolicy","requireProvenance":false}}},"lastUpdated":"2026-11-05T10:17:49.991Z","slug":"approvals"},"indexedAt":"2026-11-05T10:20:53.868Z","labels":[{"ver":1,"src":"did:web:labels.emdashcms.com","uri":"at://did:plc:cus3srdqglppyowqrw3hvjfj/com.emdashcms.experimental.package.profile/approvals","val":"listing-passed","cts":"2026-11-05T10:21:06.488Z","cid":"bafyreifcccs2275g2vseefk4l22oqp7fwxhriqvzooueovmfrp35jd4wu4"}],"handle":"moorage.dev","latestVersion":"0.3.0"},{"uri":"at://did:plc:cus3srdqglppyowqrw3hvjfj/com.emdashcms.experimental.package.profile/site-health","cid":"bafyreigm7oia4kjffwb7grx2lfm6wxzprxfp4wmzjealiujxipuaajh4e4","did":"did:plc:cus3srdqglppyowqrw3hvjfj","slug":"site-health","profile":{"$type":"com.emdashcms.experimental.package.profile","id":"at://did:plc:cus3srdqglppyowqrw3hvjfj/com.emdashcms.experimental.package.profile/site-health","type":"emdash-plugin","license":"MIT","authors":[{"url":"https://moorage.dev","name":"Moorage"}],"security":[{"email":"security@moorage.dev"}],"name":"Site Health & Broken Links","description":"Site Health for EmDash: a broken link checker for your pages, menus and links to other sites, with email alerts and a setup checklist.","keywords":["broken links","broken link checker","link checker","site health","email alerts"],"sections":{"faq":"### Why does it say \"could not check\" instead of broken?\n\nSome websites, such as LinkedIn and Instagram, turn away every automatic checker. Calling those links broken is the most common complaint about link checkers, so they are listed apart and never counted. Open one to check it yourself.\n\n### Why do links to other sites take a day?\n\nA link is called broken only when the other website says the page is gone on two checks at least a day apart. A site that is down for an hour should not send you an alert.\n\n### Will it slow my site down or annoy other websites?\n\nNo. It reads your pages a few at a time in the background, asks other websites for a few pages a minute, never the same website more than once a minute, and says who it is.\n\n### Can it change the links inside my pages for me?\n\nNot yet. For an address on your own site, \"Send visitors to another page\" fixes the link everywhere at once. For other links, it opens the page with the link named.\n\n### My site is not public yet. Does it work?\n\nPartly. Links inside your pages that point to one of your pages or posts are checked straight away, and a link to a page that is not published yet shows as broken. Links to any other address on your own site, the menus, page errors and the Checklist need the site to open its own pages, so they wait until it has a public address; until then those links are listed under \"Links we could not check\", never as broken.\n\n### Who gets the emails?\n\nThe first Admin who opens Site Health, plus anyone you add under Checklist, Options. Your site needs an email service for EmDash; the Checklist's \"Send a test email\" tells you whether it has one.\n\n## Why do the checks and emails use the site's old address?\n\nPlugins keep the site address EmDash had when the server started. After changing the site's address, restart the site.\n","security":"## Reporting a problem\n\nEmail **security@moorage.dev**, not a public issue. Say which version you use, what you found and how to reproduce it. Fixes go into the latest release; update to it to receive them. The source is public on GitHub.\n\n## What it keeps and sends\n\nIt keeps the problems it found and your choices (marked as fine, alert addresses) in the site's own database. It requests the site's own pages and the addresses your pages link to, and nothing else; it sends no page content to anyone. Alerts go by the site's own email to the addresses you enter. Adding a redirect happens only when you press Fix.\n","description":"Site Health is a broken link checker for EmDash that tells you what is broken before a customer does: a dead link in the menu, a page that shows an error, a map that will not load.\n\n- **Broken links, grouped by how bad.** Every page (the site menu and footer), one page (a page that shows an error, or loads a map or form over an insecure connection), one link (a link inside a page that goes nowhere). One row per broken address, with every page it was found on.\n- **Fix in one step.** Send visitors from a dead address on your site to a page you pick from a list, and the link is fixed everywhere it appears. Or open the page with the link named. Check any row again at once, or mark it as fine.\n- **Links to other sites, checked politely.** A few at a time, never one site more than once a minute. A link is called broken only when the other website says the page is gone, twice, a day apart. Sites that turn away automatic checkers (LinkedIn, Instagram) are listed as \"could not check\", never as broken.\n- **Email when it matters.** At once when a page shows an error or a menu link breaks, and a weekly list of only what is new. Several addresses, the site's name first in the subject, never more than one email a day of each kind.\n- **A setup checklist** for whoever looks after the site: secure address, with and without www, the \"page not found\" page, search engines allowed in, scheduled tasks running, email working.\n- **A dashboard widget** with the count.\n\nNo account, no key, nothing sent to anyone but the websites your pages link to. Editors see the links they can fix; page errors and setup are for Admins.\n","installation":"Install from **Plugins** in your EmDash admin, or pin it in code:\n\n```sh\nnpm install @moorage/plugin-site-health\n```\n\n```js\n// astro.config.mjs\nimport siteHealth from \"@moorage/plugin-site-health\";\n\nemdash({ sandboxed: [siteHealth] });\n```\n\nThen open **Tools > Site Health** in the admin sidebar. The first check starts by itself and takes about 20 minutes for a site with 40 pages; links to other sites follow a day later.\n\nSite Health needs EmDash's scheduled tasks. On Cloudflare that means a Cron Trigger on the Worker; the Checklist tab says so if they are not running.\n"},"extensions":{"com.emdashcms.experimental.package.profileExtension":{"$type":"com.emdashcms.experimental.package.profileExtension","repository":"https://github.com/moorage-dev/moorage","releasePolicy":{"$type":"com.emdashcms.experimental.package.profileExtension#releasePolicy","requireProvenance":false}}},"lastUpdated":"2026-11-05T10:17:37.948Z","slug":"site-health"},"indexedAt":"2026-11-05T10:20:51.945Z","labels":[{"ver":1,"src":"did:web:labels.emdashcms.com","uri":"at://did:plc:cus3srdqglppyowqrw3hvjfj/com.emdashcms.experimental.package.profile/site-health","val":"listing-passed","cts":"2026-11-05T10:21:02.814Z","cid":"bafyreigm7oia4kjffwb7grx2lfm6wxzprxfp4wmzjealiujxipuaajh4e4"}],"handle":"moorage.dev","latestVersion":"0.2.0"},{"uri":"at://did:plc:cus3srdqglppyowqrw3hvjfj/com.emdashcms.experimental.package.profile/seo","cid":"bafyreicufifp4rarpou7ijtkto56qllbagavaugtuhc6nih6jhhg4hw3o4","did":"did:plc:cus3srdqglppyowqrw3hvjfj","slug":"seo","profile":{"$type":"com.emdashcms.experimental.package.profile","id":"at://did:plc:cus3srdqglppyowqrw3hvjfj/com.emdashcms.experimental.package.profile/seo","type":"emdash-plugin","license":"MIT","authors":[{"url":"https://moorage.dev","name":"Moorage"}],"security":[{"email":"security@moorage.dev"}],"name":"SEO & Schema Markup","description":"SEO for EmDash: schema markup, JSON-LD structured data, Google preview, SEO checks, noindex, WordPress redirects import. Yoast alternative.","keywords":["seo","schema markup","structured data","yoast alternative","wordpress redirects"],"sections":{"faq":"## How do I add schema markup to EmDash?\n\nInstall this plugin. Every page gets structured data (JSON-LD) straight away; say who the site is for on the SEO page to make it accurate.\n\n## Is there a Yoast alternative for EmDash?\n\nYes, in two parts. EmDash itself does titles, descriptions, sitemaps and canonicals. This plugin adds the schema graph, the list of pages to fix, the Google preview and the redirects import. There is no traffic light and no focus keyphrase.\n\n## How do I import redirects from WordPress?\n\nExport them as CSV from Redirection, Yoast SEO Premium or Rank Math (Rank Math's CSV export is in its paid version; Redirection's JSON, .htaccess and Nginx exports are not read), open SEO > Redirects > Import from WordPress, paste the list and press \"Check list\". Nothing is added until you confirm.\n\n## How do I noindex a collection in EmDash?\n\nOpen SEO > Settings > Content types and choose \"Hide from Google and other search engines\" for that content type.\n\n## Is it free?\n\nYes, on any number of sites, your own or your clients', with no account and no key. It is open source under the MIT License: you may use, change, copy and share the code, in paid work too, as long as the copyright notice stays with it.\n\n## Is there an SEO score?\n\nNo, on purpose. You get a list of things to fix that can reach zero.\n\n## Does it add local business schema with opening hours?\n\nYes. Choose \"A local business customers visit\" on the SEO page and fill in the address, phone number and opening hours. Until there is an address the site is described as an organisation, because Google requires one.\n\n## Will my pages get rich results on Google?\n\nThe markup makes pages eligible; Google decides what it shows. Admins can open Google's Rich Results Test from the Google preview panel.\n\n## Where do I write the meta description?\n\nIn EmDash's own SEO panel on each entry. This plugin lists the pages that have none.\n","security":"## Reporting a problem\n\nEmail **security@moorage.dev**, not a public issue. Say which version you use, what you found and how to reproduce it. Fixes go into the latest release; update to it to receive them. The source is public on GitHub.\n\n## What it keeps and sends\n\nIt keeps one small record per entry in the site's own database: dates, authors, the page type and any extra structured data you add. It sends nothing to any outside service and loads no outside scripts. Redirects change only when you add, edit or remove one, or confirm an import.\n","description":"EmDash already handles titles, descriptions, sitemaps, canonicals and social tags. This plugin adds structured data, page checks and redirects import. No account and no key.\n\n## What it does\n\n- **Schema markup (structured data, JSON-LD).** One linked graph per page in place of EmDash's single object: Organization, Person or a local business with address and opening hours; WebSite; WebPage; blog posts and articles with their authors; services; team members; breadcrumbs.\n- **A page type for every collection**, guessed from its name and changeable per collection or per page.\n- **Pages to fix.** A list of pages with a missing description (an excerpt counts as one), a shared title or a canonical pointing elsewhere, each with a link to the page. No score and no traffic light: a list that can reach zero.\n- **Google preview** in the entry editor: the address, title and description as Google may show them, with up to three things to fix.\n- **Hide from search (noindex)** per collection, with a warning on the SEO page whenever something is hidden.\n- **Redirects.** Add, change and remove redirects, or paste a CSV export from Redirection, Yoast SEO Premium or Rank Math.\n\n## What it does not do\n\n- It does not write the page title, the sitemap or robots.txt; EmDash core and your theme do.\n- No keyword density or readability score: Google uses neither.\n- No FAQ, Product or Event schema yet; use a `json_ld` field for those. What you paste joins the same graph.\n- The checks describe the published page, not what you are typing.\n\n## Limits\n\nOne business location. EmDash has no regular-expression redirects: exact addresses and \"everything under a folder\" are converted, other patterns are listed for you to rewrite.\n\n## Permissions\n\nRead content, bylines and the content schema, to check pages and build the markup. Manage redirects: the ones you add, change or remove on the Redirects tab, and an import only after you confirm it. An import never changes a redirect that already exists.\n","installation":"Install from **Plugins** in your EmDash admin, or pin it in code:\n\n```sh\nnpm install @moorage/plugin-seo\n```\n\n```js\n// astro.config.mjs\nimport seo from \"@moorage/plugin-seo\";\n\nemdash({ sandboxed: [seo] });\n```\n\nThen open **SEO** in the admin sidebar. It works before you change anything; answering one question about who the site is for makes the result more accurate.\n"},"extensions":{"com.emdashcms.experimental.package.profileExtension":{"$type":"com.emdashcms.experimental.package.profileExtension","repository":"https://github.com/moorage-dev/moorage","releasePolicy":{"$type":"com.emdashcms.experimental.package.profileExtension#releasePolicy","requireProvenance":false}}},"lastUpdated":"2026-11-05T10:17:24.645Z","slug":"seo"},"indexedAt":"2026-11-05T10:20:50.062Z","labels":[{"ver":1,"src":"did:web:labels.emdashcms.com","uri":"at://did:plc:cus3srdqglppyowqrw3hvjfj/com.emdashcms.experimental.package.profile/seo","val":"listing-passed","cts":"2026-11-05T10:17:37.290Z","cid":"bafyreicufifp4rarpou7ijtkto56qllbagavaugtuhc6nih6jhhg4hw3o4"}],"handle":"moorage.dev","latestVersion":"0.2.0"},{"uri":"at://did:plc:3g3mjpfnhqr4icd3ukfgyz4s/com.emdashcms.experimental.package.profile/image-alt-review","cid":"bafyreifd3ji3dkqnieqfgieyvgew6exerh64jhiciatdsojdqozvvkqfhq","did":"did:plc:3g3mjpfnhqr4icd3ukfgyz4s","slug":"image-alt-review","profile":{"$type":"com.emdashcms.experimental.package.profile","id":"at://did:plc:3g3mjpfnhqr4icd3ukfgyz4s/com.emdashcms.experimental.package.profile/image-alt-review","type":"emdash-plugin","license":"MIT","authors":[{"name":"LLL Inc.","email":"contact@lx3.cafe"}],"security":[{"email":"contact@lx3.cafe"}],"name":"LLL Saved Image Alt Review / 保存画像alt点検","description":"Review saved top-level image and gallery alt sources, coverage and positions; manually fix and re-review.","keywords":["alt","images","gallery","review","アクセシビリティ"],"sections":{"faq":"## Does a nonempty alt pass accessibility checks?\nNo. Review its meaning manually. Empty alt can be correct for decorative images.\n\n## Why does an empty own alt show text?\nThe standard resolver falls back to the saved inline asset alt. Whitespace is preserved. The current media-library record is not fetched.\n\n## Why are my latest edits absent?\nSave first, then explicitly re-review. Published entries use live data and exclude pending edits. A changed or missing saved revision fails safely.\n\n## Does it inspect every image on the page?\nNo. Only standard top-level image/gallery slots in the selected saved Portable Text field. Featured, theme, HTML, nested custom and other fields are excluded or marked partial.\n\n## Can I copy or automatically fix the results?\nSelect and copy the entire plain Code report manually. Edit with the native image/gallery controls and save. There is no automatic patch or clipboard access.\n\n## Why can access or a large report fail?\nHost role, owner, token scope and CSRF checks intersect. Input, slot, metadata, report and Blocks bounds all apply; combined maxima can exceed the output budget. No earlier or truncated report is returned.\n","security":"Private editor panel only. Required reads: content:read, schema:read, broad content:revisions:read. This implementation requests only the current draft pointer once and performs no history listing. The host supplies trusted entry identity; the private 16 KiB JSON POST also requires content:edit_own_drafts, owner/edit-any authorization, CSRF and token scopes.\n\nNo content/media write, public route, network, storage, image fetch, telemetry or clipboard access. Output allowlists exclude URLs/captions/arbitrary metadata. Controls and bidi values use reversible JSON literals. A final identity/version/status/pointer fence detects observed concurrent changes, not atomic snapshot isolation. Invalid or oversized results remove the whole report. Report vulnerabilities to contact@lx3.cafe.\n","changelog":"## 0.1.0\n\nInitial saved Portable Text image/gallery alt review: explicit EN/JA editor panel, current saved source and final identity fence, slot coverage and source diagnostics, bounded full copy report, and manual native-edit/save/re-review workflow.\n","installation":"## Install and review\n\nInstall and enable LLL Saved Image Alt Review in EmDash 1.4. Open an entry you can edit. Expand **Saved Image Alt Review / 保存画像alt点検**, choose one saved Portable Text field and English or Japanese, then explicitly review.\n\nCheck the reported live/draft source, coverage and slot positions. Edit image alt using the normal image toolbar or gallery settings, save the entry, and review again. Unsaved typing is excluded; published entries exclude pending edits. Select and copy the full Code report manually.\n\nThis is a scoped saved-data review, not image understanding, a whole-page scan or a quality/accessibility pass. Empty alt may be appropriate. Unknown shapes remain partial. The revision read permission is broad although this implementation fetches only the current draft pointer, never history. No content/media writes, storage, network, public route or clipboard API is used. See the bundled README for exact bounds, owner permissions and snapshot limitations.\n"},"lastUpdated":"2026-11-05T10:08:18.341Z","slug":"image-alt-review"},"indexedAt":"2026-11-05T10:08:25.942Z","labels":[{"ver":1,"src":"did:web:labels.emdashcms.com","uri":"at://did:plc:3g3mjpfnhqr4icd3ukfgyz4s/com.emdashcms.experimental.package.profile/image-alt-review","val":"listing-passed","cts":"2026-11-05T10:08:30.431Z","cid":"bafyreifd3ji3dkqnieqfgieyvgew6exerh64jhiciatdsojdqozvvkqfhq"}],"handle":"mrhandsome39.bsky.social","latestVersion":"0.1.0"},{"uri":"at://did:plc:tjevjjo6l4vtj3bqd2akrya7/com.emdashcms.experimental.package.profile/exif","cid":"bafyreiewtddpqnszavkjbwjpqzae2psdsan2tpmjtg36j5leua3ponvtcq","did":"did:plc:tjevjjo6l4vtj3bqd2akrya7","slug":"exif","profile":{"$type":"com.emdashcms.experimental.package.profile","id":"at://did:plc:tjevjjo6l4vtj3bqd2akrya7/com.emdashcms.experimental.package.profile/exif","type":"emdash-plugin","license":"MIT","authors":[{"name":"Angus Zhu"}],"security":[{"url":"https://anguszhu.com"}],"name":"Photo EXIF Extractor","description":"Extracts camera, lens, exposure, timestamp, and optional GPS EXIF metadata from photos in the EmDash media library.","keywords":["exif","photo","metadata","camera","photography"],"sections":{"description":"Photo EXIF Extractor automatically inspects photos uploaded to your EmDash media library and extracts camera metadata, lens details, exposure settings, timestamps, and optional GPS coordinates.\n\n### Key Capabilities\n\n- **Automatic Extraction**: Listens to `media:afterUpload` and indexes photo metadata in sandboxed storage.\n- **Multi-Format Support**: Zero-dependency Web-API parser supporting JPEG, TIFF, WebP, PNG, HEIC, HEIF, and AVIF.\n- **Privacy by Default**: GPS coordinates are excluded by default and only extracted when explicitly enabled.\n- **Admin Dashboard (`/exif`)**: Browse photos, inspect full camera/exposure details, filter by camera brand, and batch-scan existing media library items.\n- **Public & Private APIs**: Readily fetch metadata in theme templates and client frontends.\n\n### How to Use\n\n1. **Automatic Extraction**: Upload any photo to the EmDash media library. The plugin parses EXIF metadata in the background.\n2. **Admin Dashboard**: Go to **Photo EXIF** (`/exif`) in the admin navigation. You can view indexed photos, filter by camera make, inspect exposure values, and run batch scans (\"Scan Missing Photos\" or \"Rescan All Photos\").\n3. **Frontend Display**: Query photo EXIF from your theme or client application:\n\n```ts\nconst res = await fetch(\"/_emdash/api/plugins/exif/get?mediaId=\" + mediaId);\nconst { data } = await res.json();\nif (data.found && data.exif) {\n  // Preformatted summary\n  console.log(data.exif.summary);\n  // \"Sony ILCE-7M4 · FE 35mm F1.4 GM · 35mm · f/1.4 · 1/250s · ISO 100\"\n\n  // Structured camera and exposure objects\n  console.log(data.exif.camera);\n  console.log(data.exif.exposure);\n}\n```\n","installation":"### Installation\n\nInstall the plugin from the EmDash plugin registry:\n\n```sh\npnpm add @anguszyu.bsky.social/exif\n```\n\n### Registration\n\nAdd `exif` to your site's sandboxed plugins in `emdash.config.ts`:\n\n```ts\nimport { defineConfig } from \"emdash\";\nimport exif from \"@anguszyu.bsky.social/exif\";\n\nexport default defineConfig({\n  sandboxed: [exif],\n});\n```\n\n### Configuration\n\nConfigure options under **Settings -> Plugins -> Photo EXIF Extractor** in the EmDash admin console:\n\n- `autoExtractOnUpload` (default `true`): Automatically extract EXIF on upload.\n- `includeGps` (default `false`): Enable to store GPS coordinates (latitude, longitude, altitude).\n- `includeRawTags` (default `true`): Store raw TIFF/IFD tags alongside normalized metadata.\n"},"lastUpdated":"2026-11-05T09:51:44.615Z","slug":"exif"},"indexedAt":"2026-11-05T09:51:51.720Z","labels":[{"ver":1,"src":"did:web:labels.emdashcms.com","uri":"at://did:plc:tjevjjo6l4vtj3bqd2akrya7/com.emdashcms.experimental.package.profile/exif","val":"listing-passed","cts":"2026-11-05T09:51:57.436Z","cid":"bafyreiewtddpqnszavkjbwjpqzae2psdsan2tpmjtg36j5leua3ponvtcq"}],"handle":"anguszyu.bsky.social","latestVersion":"0.2.0"},{"uri":"at://did:plc:3g3mjpfnhqr4icd3ukfgyz4s/com.emdashcms.experimental.package.profile/live-feed","cid":"bafyreie36mo5rcwlmjnfkhab6voqakwfxlnoeaen27ysgarovgsofpdcx4","did":"did:plc:3g3mjpfnhqr4icd3ukfgyz4s","slug":"live-feed","profile":{"$type":"com.emdashcms.experimental.package.profile","id":"at://did:plc:3g3mjpfnhqr4icd3ukfgyz4s/com.emdashcms.experimental.package.profile/live-feed","type":"emdash-plugin","license":"MIT","authors":[{"name":"LLL Inc.","email":"contact@lx3.cafe"}],"security":[{"email":"contact@lx3.cafe"}],"name":"LLL Live Feed / 公開記事RSS配信","description":"Publish one live RSS2 feed from explicitly selected public fields and exact locale.","keywords":["rss","feed","subscribe","公開記事"],"sections":{"faq":"### Is saving a draft enough to publish?\nNo. Save changes only the draft. Review and explicitly Enable feed to replace published settings. Disable stops the public endpoint; Clear draft leaves any enabled published configuration intact.\n\n### What is included?\nOne collection, one exact non-empty locale, latest20 published entries, title, URL, opaque GUID, valid past publication date, and optional plain summary. Blank/non-string titles use the public entry ID as a label and the private review reports this. Full body, revisions, SEO, author data and other locales are not emitted.\n\n### Are routable fields automatically safe to expose?\nNo. You must verify anonymous access and selected fields in your actual theme. Custom member-only policies cannot be detected automatically. Null locales are excluded, without fallback to the site locale.\n\n### Do stopped or deleted articles disappear immediately?\nA commit completed before GET is excluded from that next response. There is a final configuration/list recheck, not an atomic snapshot. RSS reader polling and previously downloaded copies are outside plugin control.\n\n### Does this track readers or need external credentials?\nNo visitor tracking, mail, network allowlist, API key, publication write or cron. It stores one bounded plugin KV configuration key. The SDK content:read grant is broader than the emitted projection.\n","security":"Capabilities: content:read and schema:read. The SDK grants are broader than this emitted projection. Code lists only one selected collection with published status and exact locale, never loads revisions and never publishes/updates CMS content. No external hosts, cron, email, AI or visitor tracking.\n\nPrivate POST uses host plugins:manage and CSRF. Fixed plugin KV key live-feed-config-v1 stores at most16KiB of namespace/draft/published/enable settings, never article copies, actor information or credentials. One-key CAS binds saves and immutable confirmations. Public GET/HEAD has no-store, raw RSS2 MIME and host security headers. Invalid/off/race/read/budget states fail closed without old-feed fallback.\n\nSelected strings are XML1.0 validated. RSS item descriptions use HTML then XML escaping; titles/channel text use XML escaping. Reader implementations differ: normalized text must not be treated as HTML-safe by a consumer. No DOCTYPE/ENTITY/CDATA or arbitrary HTML is generated. Contact contact@lx3.cafe for support and vulnerability reports.\n","changelog":"### 0.1.0\nInitial single-feed release: explicit public-field acknowledgement, private draft/review/CAS enable, exact locale/live projection, stable opaque GUIDs, bounded RSS2 raw GET/HEAD and distinct disable/clear operations.\n","installation":"Requires EmDash1.4 and a supported sandbox runner. Tested on Node22, Astro7.3.2 and sandbox-workerd0.9.6. Cloudflare deployment has not been accepted in this release.\n\n1. Install and enable LLL Live Feed. Open its Live Feed admin page with permission to manage plugins.\n2. Edit the saved draft, choose a routable collection, continue, then open a fresh form. Select schema-backed plain string/text fields, one exact non-empty locale, title and description. Null-locale legacy content is excluded.\n3. Verify that the selected articles and fields are anonymously public in your real theme. Do not select member-only or custom-authenticated content. Explicitly acknowledge this before saving.\n4. Save draft only. Review the exact saved draft, current published configuration, RSS URL and latest public projection. Enable feed to publish that reviewed snapshot.\n5. Copy the exact URL shown on the page into an RSS reader. No theme code or cron is required for the feed endpoint; article links still require your real theme routes.\n\nClear draft and Disable public feed are separate. Reload discards unsaved form values. A competing save requires a reload and fresh review. Keep namespace/KV state when moving installations if GUID continuity matters. Reinstalling without state can reset GUID identity.\n"},"lastUpdated":"2026-11-05T08:53:28.437Z","slug":"live-feed"},"indexedAt":"2026-11-05T08:53:35.555Z","labels":[{"ver":1,"src":"did:web:labels.emdashcms.com","uri":"at://did:plc:3g3mjpfnhqr4icd3ukfgyz4s/com.emdashcms.experimental.package.profile/live-feed","val":"listing-passed","cts":"2026-11-05T08:53:40.468Z","cid":"bafyreie36mo5rcwlmjnfkhab6voqakwfxlnoeaen27ysgarovgsofpdcx4"}],"handle":"mrhandsome39.bsky.social","latestVersion":"0.1.0"},{"uri":"at://did:plc:3g3mjpfnhqr4icd3ukfgyz4s/com.emdashcms.experimental.package.profile/scheduled-unpublish","cid":"bafyreie2pkrx7c3lxril3jll6dqlokjb3ygemr2jby4ryy4jbe3pxsx2fa","did":"did:plc:3g3mjpfnhqr4icd3ukfgyz4s","slug":"scheduled-unpublish","profile":{"$type":"com.emdashcms.experimental.package.profile","id":"at://did:plc:3g3mjpfnhqr4icd3ukfgyz4s/com.emdashcms.experimental.package.profile/scheduled-unpublish","type":"emdash-plugin","license":"MIT","authors":[{"name":"LLL Inc.","email":"contact@lx3.cafe"}],"security":[{"email":"contact@lx3.cafe"}],"name":"LLL Scheduled Unpublish / 記事の予約公開終了","description":"Schedule one saved published entry to return to draft, with version guards and a scheduler probe. 保存公開記事の期限終了。","keywords":["unpublish","expiry","schedule","公開終了"],"sections":{"faq":"What does this do? It returns one published article to draft at a confirmed server UTC deadline. Native scheduling publishes; this job ends publication. One active target/reservation, not a deadline-only list.\n\nDoes cron presence prove readiness? No. An automatic one-shot probe must reach this plugin hook, with a receipt no older than5minutes. This proves recent delivery, not future uptime. Unsupported/no-op hosts cannot arm without that receipt.\n\nWhat if content changes? Changed revision/identity/locale/status/scheduled publication is skipped. A final SDK revision guard blocks a racing update. It never substitutes the latest version.\n\nCan I cancel? Before claim, cancellation wins by CAS. After claim cancellation cannot be promised. KV/cron/publication are not one transaction. There is no automatic republishing.\n\nWhat is unknown? A write or receipt was not confirmed or an operation stopped. A current draft is not proof of past success. Observe target/task, then explicitly close unknown without a success claim. See README for recovery details.\n\nWhat is stored? Two plugin KV keys: bounded probe and reservation/receipt. No article body, SEO, actor email, credentials or history. item.data processing≤256KiB after SDK retrieval, ID≤128ASCII, opaque revision≤2048B. Display labels abbreviate; confirmation identity/revision does not.\n\nTiming and host? Exact UTC YYYY-MM-DDTHH:mm:ssZ,30seconds–366days ahead. Long-lived single-process Node initial support; no Cloudflare/cluster/scale-to-zero guarantee. Normal acceptance≤90seconds after due, not an exact deadline or emergency/legal guarantee. Cache/mirrors may retain old content.\n\n日本語: probe登録と実到達、previewと予約済み、正規receiptと読取時点のdraftを分けます。unknownは成功ではなく、必要なら確認後に明示終了してください。\n","security":"Private admin POST requires plugins:manage and normal host CSRF. There is no public plugin route. Requested grants: content:read/schema:read/content:publish. The publication grant can also publish/schedule; code calls only version-guarded unpublish of the confirmed target. No content:write, revision-history reading, user directory, media, email, network, AI, analytics or billing service.\n\nTwo fixed plugin-scoped KV keys persist bounded probe/reservation/receipt state. Cron task names include unpredictable tokens. Probe and reservation use CAS; old or duplicate tasks cannot act on another reservation. Single-key CAS is atomic; KV, cron registration and publication are not one transaction. A missing receipt is never manufactured from a later draft. Private content returned by the SDK is not logged, saved or sent externally; only bounded title labels/identity/version/status are displayed to authorized administrators.\n\nInput/body limits are enforced, displayed titles are plain text and visibly abbreviated, and stored done requires a matching updated success receipt. Explicit reservation/Cancel/unknown-close actions and host permissions govern writes. Tests use owned synthetic articles only. Normal host/infrastructure logging has its own policy. Report vulnerabilities to contact@lx3.cafe with synthetic/redacted steps, never credentials or private article content. No SLA, legal compliance or exact timing guarantee.\n","changelog":"0.1.0 — One version-bound automatic unpublish reservation, actual scheduler probe, immutable confirmation, cancellation before claim, task observations, guarded receipt/unknown handling, English/Japanese private admin and saved-article batching. Long-lived single-process Node initial support. No external services or automatic platform setup.\n","installation":"Requires EmDash1.4, a supported sandbox runner and a long-lived single-process Node host. Cloudflare, clustered and scale-to-zero hosts are unsupported in this initial release. No new platform cron setup is performed by installation.\n\nInstall from the official Registry when published. Review content:read, schema:read and the powerful content:publish grant, plugin KV persistence and cron hook. The implementation only unpublishes an explicitly confirmed target; the grant can technically publish/schedule too.\n\nOpen Scheduled Unpublish. Click Check automatic scheduler, then Refresh until an actual hook receipt is observed. Registration alone is not readiness. The receipt must be no older than5minutes. Choose a collection and saved published article; browse20-entry batches as needed. Next/First removes the form; Open batch mounts the new one. Enter exact UTC,30seconds to366days ahead. Review complete target/version/deadline, then Confirm. Preview is not a reservation.\n\nCancel only before execution claims the job. Later article edits, locale/status changes or future-publish scheduling cause a skip. The plugin never swaps in a newer version. Refresh/reconcile shows actual task presence and the current target; unknown is not success. Read the full README for failures, restart and explicit unknown closure.\n\nAfter due, check the recorded receipt, native draft with preserved body and anonymous visitor URL. Normal Node acceptance allows90seconds after due; exact timing, uptime and remote cache removal are not guaranteed. Not for emergency/legal/safety deadlines.\n\n記事を選び、UTCとversionを確認して明示予約します。保存編集後はskipし、最新版へ自動適用しません。Refresh後は別操作でformを開き直します。無料で1予約・1対象の予約/取消/実公開終了/結果確認まで完結します。\n"},"lastUpdated":"2026-11-05T07:46:37.844Z","slug":"scheduled-unpublish"},"indexedAt":"2026-11-05T07:46:44.998Z","labels":[{"ver":1,"src":"did:web:labels.emdashcms.com","uri":"at://did:plc:3g3mjpfnhqr4icd3ukfgyz4s/com.emdashcms.experimental.package.profile/scheduled-unpublish","val":"listing-passed","cts":"2026-11-05T07:46:48.881Z","cid":"bafyreie2pkrx7c3lxril3jll6dqlokjb3ygemr2jby4ryy4jbe3pxsx2fa"}],"handle":"mrhandsome39.bsky.social","latestVersion":"0.1.0"}],"cursor":"eyJvZmZzZXQiOjI1fQ"}