
Demo (main site): mystatus.mighil.com
Demo (embed): mystatus-demo
I haven't been on my website/blog for a while. I am back in Kerala and taking it real slow, mostly dealing with some commissioned work. We also released a few animations (I did the music), and it has been a fun journey.
This project is based on the MyGB Cloudflare Worker flow. Use the original setup guide here. A recent email exchange with Sylvia inspired this release. Sylvia made the initial draft for her own needs, and I cleaned up the code, added more features, and decided to share MyStatus with whoever is reading my blog.
What this worker.js does
- Serves a public status page with your latest updates
- Provides an admin login to post, edit, manage, and delete entries
- Stores site settings (branding, intro text, nav links, custom CSS)
- Exposes a small embed script (
/client.js) so you can show the stream on other sites - Generates an Atom feed (
/feed.xml) and sitemap (/sitemap.xml) - Lets you export your data as JSON (
/data.json) and CSV (/data.csv) - Optional R2 media (experimental): bind a bucket as
MEDIA, use Media in the admin nav (/admin/media) to upload and manage files, serve them publicly at/media/<filename>, and reference them in statuses (for examplewith the built-in renderer).
Features
- Single-file Worker (
worker.js) for simple deployment - D1-backed storage for entries and settings
- R2-backed object storage for media
- Markdown-style rendering for posts (links, images, emphasis, code, strikethrough), with optional
markedrendering viaMD_SCRIPT=true - Embed widget support with public API + configurable API origin (
API_URL) - Pagination for older posts ("Load More")
- Clickable status cards with dedicated permalink pages (
/<id>) - Permalink pages (
/<id>) share the same public header/footer experience as the index - Admin entries table includes edit flow (
/admin/entries/edit?entry=<id>) and delete actions - Configurable SEO metadata (description, canonical URL, social image, indexing toggle)
What to do
- Follow the MyGB guide step by step (create D1, create Worker, bind DB, set secrets/vars, deploy).
- When the guide asks for the Worker code, replace the script with this repo's
worker.js. - Deploy and open your Worker URL.
- Go to
/loginand sign in with your configured admin password.
Important note about database setup
Ignore the manual DB initialization section from the previous tutorial for this version. Just create the D1 database and bind it as DB. This Worker initializes the required tables automatically on first load.
Embedded stream (/client.js): pagination
The widget loads the first page with GET /api/entries (up to 10 entries, newest first). If the API returns nextCursor, a Load more button appears; each click fetches GET /api/entries?cursor=<id> and appends rows until there are no more pages (same contract as the home page Load More).
Styling the embedded stream (/client.js)
The embed injects markup under your [data-gb] container. Add CSS on the site that embeds the widget.
| Class | Role |
|---|---|
.gb-widget |
Root of the widget (default: font-family / color inherit) |
.gb-entries |
Wraps the list |
.gb-entries-list |
List container (entries are appended here) |
.gb-entry |
One status block (<article>) |
.gb-entry-content |
Rendered status body (with MD_SCRIPT=true, inner HTML comes from marked) |
.gb-entry-meta |
Footer row for each entry |
.gb-entry-date |
Timestamp |
.gb-loading |
Shown while fetching |
.gb-no-entries |
Shown when there are no posts |
.gb-error |
Shown when the API request fails |
.gb-load-more-wrap |
Wrapper for the Load more control (hidden when there is no next page) |
.gb-load-more-btn |
Load more button |
Example CSS snippet:
<style>
.gb-entry {
border: 1px solid color-mix(in srgb,var(--text-color)10%,transparent);
padding: 0 20px 20px;
border-radius: 10px;
margin-bottom: 20px;
}
.gb-entry-meta {
font-family: var(--font-secondary);
font-size: .9em;
color: color-mix(in srgb,var(--text-color) 80%,transparent);
font-style: normal;
}
</style>
Experimental: R2 media (optional)

- Create an R2 bucket and bind it to the Worker with binding name
MEDIA. - In the admin nav, open Media (
/admin/media): upload files, copy the public URL, or delete objects from the bucket. - Files are served at
/media/<filename>(one URL path segment; names are normalized to letters, digits,.,_, and-). - In statuses, use same-origin paths such as
or[label](/media/doc.pdf). The built-in renderer allows these/media/...paths alongsidehttp(s):URLs. WithMD_SCRIPT=true, you can also use fullhttps://URLs to your site.
Upload limits (experimental): up to 15 MB per file; types allowed include common raster images (not SVG), video/mp4, video/webm, audio/mpeg / mp3 / wav / webm, and application/pdf.
Credits
This direction was Sylvia's idea, and she cleaned it up for her use case (kudos to her). I reviewed it and am now releasing a version with extended features. Sylvia also added markdown support for statuses, meaning you can write status posts directly in Markdown.
New variables in this version
ALLOWED_ORIGINS
Set it as a comma-separated list of full origins allowed for cross-origin access to public endpoints.
Example:
ALLOWED_ORIGINS=https://myblog.com,https://www.myblog.com,http://localhost:3000
MD_SCRIPT
MD_SCRIPT controls the markdown rendering engine.
MD_SCRIPT=true-> loadsmarkedfrom CDN in the browser (MARKED_BROWSER_SCRIPT_URL)MD_SCRIPT=false(or unset) -> uses the built-in basic markdown renderer inworker.js
Configure MyStatus however you like. Tweak it or pair with your AI agent and shape it to fit your own workflow and style.