stdoutfor the web — one command, one URL, done.
Publish markdown to a stable URL. Built for AI agents, usable by humans.
Live at bul.sh
- Live service:
https://bul.sh - Publish model: pre-render once on publish, then serve cached HTML
- Content storage: public Blob
- Metadata storage: private Blob
- Read path on Vercel: Hono + aggressive edge caching
- Published pages are effectively immutable unless explicitly republished
git clone https://github.com/Restuta/pubmd.git
cd pubmd
npm install
npm run build
# Local CLI usage from source
node dist/src/cli/main.js --helpmacOS / Linux:
curl -fsSL https://bul.sh/install | shWindows (PowerShell):
irm https://bul.sh/install.ps1 | iexnpm (any platform):
npx pubmd --helpDefault behavior (macOS/Linux):
- installs
pubmdto~/.local/bin - does not require
sudo - prints a PATH hint if
~/.local/binis not already on your shell PATH
Default behavior (Windows):
- installs
pubmd.exeto%LOCALAPPDATA%\pubmd - adds to user PATH automatically
# Claim your namespace
node dist/src/cli/main.js claim myname --api-base https://bul.sh
# Publish
node dist/src/cli/main.js publish notes.md --api-base https://bul.sh
# → https://bul.sh/myname/notes
# Re-publish (same URL, updated content)
node dist/src/cli/main.js publish notes.md --api-base https://bul.sh
# Pipe from stdin
cat report.md | node dist/src/cli/main.js publish --slug weekly-report --namespace myname --api-base https://bul.sh
# List your pages
node dist/src/cli/main.js list --namespace myname --api-base https://bul.sh
# Delete a page
node dist/src/cli/main.js remove weekly-report --namespace myname --api-base https://bul.shAny AI that can run shell commands can publish. No SDK, no MCP, no API client — just a command.
A /publish skill is available. Usage:
/publish report.md
/publish report.md --slug weekly-report
Or add this to your project's CLAUDE.md:
To share long-form output as a URL, use:
pubmd publish <file.md> --api-base https://bul.sh
The command prints the live URL to stdout.
Add to AGENTS.md or system prompt:
To publish markdown to a shareable URL:
pubmd publish <file.md> --api-base https://bul.sh
To list published pages:
pubmd list --api-base https://bul.sh
If running on Windows, use npx:
npx pubmd publish <file.md> --api-base https://bul.sh
Or if installed via PowerShell:
pubmd publish <file.md> --api-base https://bul.sh
Any agent that can run curl can publish without installing anything:
# Publish raw markdown
curl -X POST -H "Authorization: Bearer $TOKEN" \
--data-binary @file.md \
https://bul.sh/api/namespaces/myname/pages/publish
# Claim namespace (one-time)
curl -s -X POST https://bul.sh/api/namespaces/myname/claimControl page metadata with YAML frontmatter:
---
title: My Report
slug: custom-url-slug
description: A short summary for social previews
noindex: false # default: true
visibility: public # stored as metadata for now
draft: true # stored as metadata for now
---
# My Report
Content here...All fields are optional. Title and description are auto-extracted from content if not specified.
Today:
title,slug,description, andnoindexaffect rendered output/metadatavisibilityanddraftare stored as page metadata, but are not yet enforced in listing/access rules
PUBLISH: CLI posts markdown -> server renders HTML once -> stores raw markdown + HTML in Blob
READ: Browser hits URL -> app serves pre-rendered HTML with aggressive Vercel edge caching
Pages are pre-rendered on publish. On Vercel, the first read may hit the app, but subsequent reads are served from edge cache for the cache window. Zero JS, system fonts, small HTML payloads.
- A publish creates a rendered snapshot
- Existing pages do not change unless explicitly republished
- Renderer/style improvements apply to newly published or explicitly republished pages
- This avoids silent regressions in old documents when styles change
node dist/src/cli/main.js claim <namespace> Claim a namespace, get API token
node dist/src/cli/main.js publish [file] [--slug <s>] [--namespace <n>] Publish or update a page
node dist/src/cli/main.js list [--namespace <n>] List your published pages
node dist/src/cli/main.js remove <slug> [--namespace <n>] Delete a page
Config stored in ~/.config/pub/config.json. File-to-URL mappings stored in .pub in the working directory.
npm run dev # local server with hot reload
npm test # run tests
npm run verify # test + lint + typecheck + build| Method | Endpoint | Auth | Description |
|---|---|---|---|
| POST | /api/namespaces/:ns/claim |
none | Claim namespace, returns token |
| POST | /api/namespaces/:ns/pages/publish |
Bearer | Publish/update a page |
| GET | /api/namespaces/:ns/pages |
Bearer | List pages |
| DELETE | /api/namespaces/:ns/pages/:slug |
Bearer | Delete a page |
| GET | /:ns/:slug |
none | Read published page (HTML) |
| GET | /:ns/:slug?raw |
none | Read raw markdown |