
Brewpage Publish
- 593 installs
- 29 repo stars
- Updated August 2, 2026
- kochetkov-ma/claude-brewcode
brewpage-publish is a Claude Code skill from kochetkov-ma/claude-brewcode that instantly publishes text, markdown, JSON, files, or multi-file sites to a public brewpage.app URL without signup for developers who need zero
About
brewpage-publish is a Claude Code skill bundled in the kochetkov-ma/claude-brewcode repository under MIT License (2025-2026) that publishes content to brewpage.app without any account signup. Developers can push plain text, markdown, JSON, individual files, or entire multi-file static sites to an immediately shareable public URL. The skill fits quick distribution of prototypes, API response samples, documentation snippets, or demo microsites directly from the agent or terminal workflow. Reach for brewpage-publish when a public link is needed faster than configuring traditional hosting, DNS, or deployment pipelines. Triggers include sharing a markdown doc, publishing a JSON payload, or deploying a small multi-file site for stakeholder review without provisioning cloud infrastructure or creating hosting accounts.
- Instant public URLs with zero account creation
- Supports plain text, markdown, JSON, single files and complete multi-file websites
- Available as slash command inside Claude Code and Cursor
- One-command publish via npx skills add kochetkov-ma/claude-brewcode
- MIT-licensed open source agent skill
Brewpage Publish by the numbers
- 593 all-time installs (skills.sh)
- +1 installs in the week ending Aug 2, 2026 (Skillselion tracking)
- Ranked #382 of 2,715 Automation & Workflows skills by installs in the Skillselion catalog
- Security screen: CRITICAL risk (skills.sh audit)
- Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/kochetkov-ma/claude-brewcode --skill brewpage-publishAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 593 |
|---|---|
| repo stars | ★ 29 |
| Security audit | 1 / 3 scanners passed |
| Last updated | August 2, 2026 |
| Repository | kochetkov-ma/claude-brewcode ↗ |
How do you publish static files to a public URL instantly?
Instantly publish text, markdown, JSON, files or entire multi-file sites to a public URL on brewpage.app without any signup.
Who is it for?
Developers who need instant public URLs for markdown docs, JSON samples, or small static sites without signup or deployment setup.
Skip if: Production applications requiring custom domains, access control, server-side logic, or long-term managed hosting infrastructure.
When should I use this skill?
The user wants to publish text, markdown, JSON, files, or a multi-file site to a shareable public URL without signup.
What you get
Public brewpage.app URL hosting published text, markdown, JSON, files, or multi-file static site content.
- public brewpage.app URL
- hosted static content
Files
brewpage-publish
Publish content to brewpage.app — free instant hosting for HTML pages, JSON documents, files, and multi-file sites. No sign-up required.
Workflow
Step 1: Parse Arguments
Extract from $ARGUMENTS:
--ttl N→ TTL in days (default:15)--entry <filename>→ entry file for SITE uploads (default: auto-detect)- Remaining text →
content_arg
Step 2: Detect Content Type
| Input | Type | API |
|---|---|---|
content_arg is a directory (test -d) | SITE | POST /api/sites (dir auto-zipped — primary path) |
content_arg ends with .zip AND file exists (test -f) | SITE | POST /api/sites (pre-built archive upload) |
content_arg is a file path AND file exists (test -f) | FILE | POST /api/files (multipart) |
content_arg starts with { or [ | JSON | POST /api/json |
| Anything else | HTML | POST /api/html (format=markdown) |
Mode rule: directory/ZIP → SITE. Single file → FILE. POST /api/sites accepts ONLY a multipart archive=@file.zip — there is no raw-folder upload, so a directory is auto-zipped on the fly (the robust default; archive sealing keeps relative paths intact). Stats per type — SITE (dir): HTML count, total size, entry file. SITE (ZIP): file size, entry override. FILE: size + MIME via file --mime-type -b. TEXT/JSON: char count.
Step 3: Show Pre-Publish Stats
For HTML/JSON/FILE:
Content: <type description> · <size> · <api endpoint>
TTL: <N> daysFor SITE: detect entry file using priority: 1) --entry flag, 2) index.html exists, 3) first .html file alphabetically.
Built-static guard (run BEFORE zipping). Publish BUILT output, never project sources:
- If the directory contains no
.htmlfile at all → FAIL with an explicit error: "No.htmlfound — build the site first, then point at the build output directory." Do not guess an entry. - If the directory looks like un-built sources (has
package.json+src/but no top-level.html) → warn and ask the user to point at the build output instead (dist/,build/,out/,_site/, orpublic/). Do not zip the source tree.
Content: site · <N> files · <total_size> · POST /api/sites
Entry: <entry_file>
TTL: <N> daysStep 4: Ask Namespace
Use AskUserQuestion:
Namespace sets the URL prefix, gallery visibility, and search-engine indexing on brewpage.app.
By default publishing is PRIVATE (unlisted): not in the public gallery and not indexed by search engines. The link is not secret, though — anyone who has it can open it (use a password to restrict access).
Choose `public` to make the page discoverable — listed in the gallery and indexed by search engines (e.g. a real site you want people to find).
Options:
1) public — listed in gallery + indexed by search engines
2) {auto-suggested 6-8 char slug} — private, link-only (default)
3) Enter custom namespace
4) Skip → use suggested slug (private)
Reply with a number or your custom namespace (alphanumeric, 3-32 chars).Auto-suggest: generate a meaningful short slug (3-16 chars, lowercase alphanumeric + hyphens) from content context:
- File → topic/purpose of the file (e.g.
api-docs,login-page,report-q2) - Text/HTML → main subject or title (e.g.
pricing,team-intro,changelog) - JSON → data type or schema name (e.g.
user-config,metrics) - Fallback → project name or directory name if content is ambiguous
Never use random strings or truncated filenames — the slug should be human-readable and describe what's being published.
Resolution:
2,4, or empty → suggested slug1→public3or any other string → use as-is
Step 5: Ask Password
Use AskUserQuestion:
Password protection (if set, page is hidden from gallery):
Options:
1) No password (default)
2) Random: {generated 6-char password, e.g. "kx7p2m"}
3) Enter custom password (min 4 chars)
4) Skip → no password
Reply with a number or your custom password.Generate random password EXECUTE using Bash tool:
LC_ALL=C tr -dc 'a-z0-9' < /dev/urandom | head -c6 2>/dev/nullResolution:
1,4, or empty → no password2→ use generated random password3or custom text → use as-is
Step 6: Publish and Save Token (secure)
SECURITY: The ownerToken MUST NEVER appear in conversation output. The bash blocks below handle curl + token parsing + history save atomically; the LLM sees only the URL. Each block setsPASS_Hfirst (empty array when no password) and uses"${PASS_H[@]}"quoted — passwords are never string-interpolated into the command. The site-dir zip excludes (.git/,.env*, etc.) are also a secret-leak safeguard — they keep credentials and VCS data out of the published archive.
6a. Init history file (run once, before the publish block) — EXECUTE using Bash tool:
HISTORY_FILE=".claude/brewpage-history.md"
if [ ! -f "$HISTORY_FILE" ]; then
mkdir -p "$(dirname "$HISTORY_FILE")"
cat > "$HISTORY_FILE" <<'HEADER'
# brewpage.app — Published Pages
> PRIVATE FILE — keep this out of version control and never share it.
> Owner tokens allow delete and in-place republish (html/json/kv/sites all support PUT).
> Delete html/json/kv: `curl -s -X DELETE "https://brewpage.app/api/{ns}/{id}" -H "X-Owner-Token: TOKEN"`
> Delete site: `curl -s -X DELETE "https://brewpage.app/api/sites/{ns}/{id}" -H "X-Owner-Token: TOKEN"`
> Update site (keep same URL): `PUT /api/sites/{ns}/{id}` with `X-Owner-Token: TOKEN` + the new bundle — the uploaded bundle fully replaces the file set (adds new files, removes absent ones, overwrites matching). The link never changes.
| Date | URL | Owner Token | TTL | Type |
|------|-----|-------------|-----|------|
HEADER
fiThen run ONE of the following publish blocks based on detected type. Each assumes HISTORY_FILE already exists from 6a.
HTML/Markdown text — EXECUTE using Bash tool:
HISTORY_FILE=".claude/brewpage-history.md"
CONTENT=$(cat <<'BREWPAGE_EOF'
{content}
BREWPAGE_EOF
)
PAYLOAD=$(jq -n --arg c "$CONTENT" '{content: $c}')
PASS_H=()
[ -n "$PASSWORD" ] && PASS_H=(-H "X-Password: $PASSWORD")
RESPONSE=$(curl -s -X POST "https://brewpage.app/api/html?ns={ns}&ttl={days}&format=markdown" \
-H "Content-Type: application/json" \
"${PASS_H[@]}" \
-d "$PAYLOAD")
URL=$(echo "$RESPONSE" | jq -r '.link // empty')
TOKEN=$(echo "$RESPONSE" | jq -r '.ownerToken // empty')
if [ -n "$URL" ]; then
[ -n "$TOKEN" ] && echo "| $(date '+%Y-%m-%d %H:%M') | [$URL]($URL) | \`$TOKEN\` | {ttl}d | html |" >> "$HISTORY_FILE"
echo "OK $URL"
else
echo "FAILED: $RESPONSE"
fiJSON — EXECUTE using Bash tool:
HISTORY_FILE=".claude/brewpage-history.md"
PASS_H=()
[ -n "$PASSWORD" ] && PASS_H=(-H "X-Password: $PASSWORD")
RESPONSE=$(curl -s -X POST "https://brewpage.app/api/json?ns={ns}&ttl={days}" \
-H "Content-Type: application/json" \
"${PASS_H[@]}" \
-d '{original_json}')
URL=$(echo "$RESPONSE" | jq -r '.link // empty')
TOKEN=$(echo "$RESPONSE" | jq -r '.ownerToken // empty')
if [ -n "$URL" ]; then
[ -n "$TOKEN" ] && echo "| $(date '+%Y-%m-%d %H:%M') | [$URL]($URL) | \`$TOKEN\` | {ttl}d | json |" >> "$HISTORY_FILE"
echo "OK $URL"
else
echo "FAILED: $RESPONSE"
fiFile — EXECUTE using Bash tool:
HISTORY_FILE=".claude/brewpage-history.md"
PASS_H=()
[ -n "$PASSWORD" ] && PASS_H=(-H "X-Password: $PASSWORD")
RESPONSE=$(curl -s -X POST "https://brewpage.app/api/files?ns={ns}&ttl={days}" \
"${PASS_H[@]}" \
-F "file=@/absolute/path/to/file")
URL=$(echo "$RESPONSE" | jq -r '.link // empty')
TOKEN=$(echo "$RESPONSE" | jq -r '.ownerToken // empty')
if [ -n "$URL" ]; then
[ -n "$TOKEN" ] && echo "| $(date '+%Y-%m-%d %H:%M') | [$URL]($URL) | \`$TOKEN\` | {ttl}d | file |" >> "$HISTORY_FILE"
echo "OK $URL"
else
echo "FAILED: $RESPONSE"
fiSite (directory) — EXECUTE using Bash tool:
HISTORY_FILE=".claude/brewpage-history.md"
PASS_H=()
[ -n "$PASSWORD" ] && PASS_H=(-H "X-Password: $PASSWORD")
TMPZIP=$(mktemp /tmp/brewpage-site-XXXXXX.zip)
# Exclude VCS, secrets, deps, editor + OS junk and sourcemaps — publish only built static assets.
(cd "{directory_path}" && zip -r "$TMPZIP" . -x '.git/*' '*/.git/*' '.env' '.env.*' '*/.env' '*/.env.*' 'node_modules/*' '*/node_modules/*' '.DS_Store' '*/.DS_Store' 'Thumbs.db' '.idea/*' '*/.idea/*' '.vscode/*' '*/.vscode/*' '.cache/*' '*/.cache/*' '*.map' '*.log')
RESPONSE=$(curl -s -X POST "https://brewpage.app/api/sites?ns={ns}&ttl={days}&entry={entry}" \
-H "User-Agent: ClaudeCode/1.0" \
"${PASS_H[@]}" \
-F "archive=@$TMPZIP")
rm -f "$TMPZIP"
URL=$(echo "$RESPONSE" | jq -r '.link // empty')
URL="${URL%/}" # strip any trailing slash — /public/<id>/ routes to brewpage landing
TOKEN=$(echo "$RESPONSE" | jq -r '.ownerToken // empty')
FCOUNT=$(echo "$RESPONSE" | jq -r '.fileCount // "?"')
if [ -n "$URL" ]; then
[ -n "$TOKEN" ] && echo "| $(date '+%Y-%m-%d %H:%M') | [$URL]($URL) | \`$TOKEN\` | {ttl}d | site ($FCOUNT files) |" >> "$HISTORY_FILE"
echo "OK $URL | Files: $FCOUNT"
else
echo "FAILED: $RESPONSE"
fiSite (ZIP file) — EXECUTE using Bash tool:
HISTORY_FILE=".claude/brewpage-history.md"
PASS_H=()
[ -n "$PASSWORD" ] && PASS_H=(-H "X-Password: $PASSWORD")
RESPONSE=$(curl -s -X POST "https://brewpage.app/api/sites?ns={ns}&ttl={days}&entry={entry}" \
-H "User-Agent: ClaudeCode/1.0" \
"${PASS_H[@]}" \
-F "archive=@{zip_file_path}")
URL=$(echo "$RESPONSE" | jq -r '.link // empty')
URL="${URL%/}" # strip any trailing slash — /public/<id>/ routes to brewpage landing
TOKEN=$(echo "$RESPONSE" | jq -r '.ownerToken // empty')
FCOUNT=$(echo "$RESPONSE" | jq -r '.fileCount // "?"')
if [ -n "$URL" ]; then
[ -n "$TOKEN" ] && echo "| $(date '+%Y-%m-%d %H:%M') | [$URL]($URL) | \`$TOKEN\` | {ttl}d | site ($FCOUNT files) |" >> "$HISTORY_FILE"
echo "OK $URL | Files: $FCOUNT"
else
echo "FAILED: $RESPONSE"
fiStep 7: Output Result
Success (bash printed OK {url}):
Published: {url from bash output}
Owner token saved to .claude/brewpage-history.mdSuccess for SITE (bash printed OK {url} | Files: {count}):
Published site: {url from bash output}
Entry: {entry_file} | Files: {count}
Owner token saved to .claude/brewpage-history.md
⚠ Share the URL exactly as printed — DO NOT append a trailing slash.
brewpage.app routes "/public/<id>/" to its own landing page, and the
redirect that saves the no-slash form does not fire for the slash-dir form.For a private (non-public) namespace, append one short line after the link (skip if the reply must stay ultra-brief): Unlisted link — anyone who has it can open it, but it's not in the gallery or search. Want it discoverable? Publish to `public`.
NEVER print the ownerToken in conversation. The token lives only in the history file.
Error (bash printed FAILED: ...):
Publish failed.Notes
- Always use absolute file paths with curl
-F "file=@...". - Use
jq -n --arg c "$CONTENT" '{content: $c}'to safely encode text content. `format` is a query param, not a body field —/api/htmlignores anyformatkey inside the JSON body and reads only?format=from the URL. Wrong location = server applies defaulthtmland stores your markdown as raw text. - TTL default is
15days. Namespace must be alphanumeric (3-32 chars), default: suggested (private) namespace. - To delete a published page, find the owner token in
.claude/brewpage-history.mdand use the delete command shown in that file's header. - To update a published site,
PUTthe new bundle to the same site URL (PUT /api/sites/{ns}/{id}) with yourX-Owner-Token— the uploaded bundle fully replaces the file set (adds new files, removes absent ones, overwrites matching) and the link never changes. No DELETE-then-POST needed. - Sites: directory is the primary input — it is auto-zipped (the only thing
POST /api/sitesaccepts), which seals relative paths. A pre-built.zipis the alternative input, uploaded as-is. Always publish BUILT output (dist/,build/,out/,_site/,public/), never project sources. - The auto-zip excludes
.git/,.env/.env.*,node_modules/, editor/OS junk, sourcemaps and logs — a deliberate secret-leak safeguard so credentials and VCS history never reach the public archive. - Entry file detection:
--entryoverride >index.html> first.htmlalphabetically. - SITE URL — NO trailing slash. API returns
.link = "https://brewpage.app/public/<id>"without trailing/. Appending/routes to brewpage.app's own landing page; the JS redirect that rescues the no-slash form does NOT fire for the slash-dir form → site becomes inaccessible. - SITE verification cannot be done via `curl`. The no-slash URL serves the BrewPage landing HTML with an inline JS redirect that only executes in a real browser. Verify with Playwright /
browser_navigate, or fetch<url>/index.htmlexplicitly.
---
Powered by
| [brewpage.app](https://brewpage.app) | Free instant hosting — HTML, JSON, files, sites, KV. No sign-up. |
| [brewcode](https://github.com/kochetkov-ma/claude-brewcode) | Claude Code plugin suite — infinite tasks, code review, skills, hooks. |
MIT License
Copyright (c) 2025-2026 Maxim Kochetkov (kochetkov-ma)
https://github.com/kochetkov-ma/claude-brewcode
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Brewpage Publish
Publish text, markdown, JSON, files, or whole multi-file sites to brewpage.app — get a public URL instantly. No sign-up.
Quick Start
1. Install:
npx skills add kochetkov-ma/claude-brewcode2. Use via slash command:
/brewpage-publish "Hello, world!"
/brewpage-publish report.md
/brewpage-publish '{"status": "ok"}'
/brewpage-publish screenshot.png --ttl 1
/brewpage-publish ./my-site --entry index.html
/brewpage-publish site.zipOr via natural language:
Publish this to brewpage
Upload report.md to brewpage.app
Deploy this directory as a siteClaude detects the content type, asks for a namespace and password interactively, calls the brewpage.app API, and returns a public URL. The owner token is saved to .claude/brewpage-history.md for later deletion.
What It Does
1. Detects content type — directory/ZIP becomes a multi-file site, single file becomes a file upload, objects/arrays become JSON, everything else becomes HTML (markdown rendered) 2. Asks namespace — interactive prompt for a short, human-readable namespace (URL slug) 3. Asks password — optional password protection (hides the page from the gallery) 4. Calls API — sends content to brewpage.app 5. Returns URL — public link ready to share 6. Saves token — owner token written to .claude/brewpage-history.md
API Coverage
| Content | Type | Endpoint |
|---|---|---|
| Text / markdown | HTML | POST /api/html?format=markdown |
| JSON object/array | JSON | POST /api/json |
| Local file | File | POST /api/files |
| Directory | Site | POST /api/sites (zipped on the fly) |
.zip archive | Site | POST /api/sites |
Sites
POST /api/sites accepts only a `.zip` archive — there is no raw-folder upload.
- Directory (primary): point at a built static directory; the skill auto-zips it and uploads. Archive sealing keeps relative paths intact.
- Pre-built `.zip` (alternative): uploaded as-is.
The auto-zip excludes .git/, .env/.env.*, node_modules/, .DS_Store, Thumbs.db, .idea/, .vscode/, .cache/, *.map and *.log — only real built assets ship; secrets and VCS data never leak into the public archive.
Built-static guard: publish build output, not sources. No .html in the directory → the skill fails and tells you to build first. A source tree (package.json + src/, no top-level .html) → the skill asks you to point at the build output (dist/, build/, out/, _site/, public/).
Entry file: --entry override > index.html > first .html alphabetically.
No trailing slash. The API returns https://brewpage.app/public/<id> without a trailing /. Appending / routes to brewpage.app's landing page and breaks the link. Share the URL exactly as printed. Site URLs cannot be verified with plain curl (the no-slash URL serves an inline JS redirect that only runs in a browser) — verify with a real browser or fetch <url>/index.html.
TTL
Default time-to-live is 15 days (max 30). Override with --ttl N (days):
/brewpage-publish report.md --ttl 1
/brewpage-publish '{"data": [1,2,3]}' --ttl 30Owner Token & Privacy
The owner token is never printed in conversation — the skill's bash blocks curl the API, parse the token, and append it to .claude/brewpage-history.md directly. Only the public URL is shown.
.claude/brewpage-history.md is a private file (keep it out of version control). Use the token to delete a page:
# html / json / kv
curl -X DELETE "https://brewpage.app/api/{ns}/{id}" -H "X-Owner-Token: {token}"
# site
curl -X DELETE "https://brewpage.app/api/sites/{ns}/{id}" -H "X-Owner-Token: {token}"Part of Brewcode
This skill is part of brewcode — a development platform for Claude Code with infinite focus tasks, agents, quorum reviews, and knowledge persistence.
claude plugin marketplace add https://github.com/kochetkov-ma/claude-brewcode
claude plugin install brewcode@claude-brewcodeLicense
MIT
Related skills
How it compares
Choose brewpage-publish over full static-hosting setup when you need a no-signup public URL for markdown, JSON, or small multi-file demos in seconds.
FAQ
Does brewpage-publish require an account?
brewpage-publish publishes text, markdown, JSON, files, and multi-file sites to brewpage.app without any signup. The skill targets developers who need an immediate public URL for demos or docs without creating hosting accounts or configuring deployment pipelines.
What content types can brewpage-publish deploy?
brewpage-publish handles plain text, markdown, JSON, individual files, and entire multi-file static sites. Each publish action returns a public brewpage.app URL suitable for sharing prototypes, API samples, or documentation with stakeholders.
Is Brewpage Publish safe to install?
skills.sh reports 1 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.