secubox-deb/packages/secubox-metablogizer
CyberMind 7c273e2132
feat(metablogizerctl): forge tor noun verbs — Emancipate (closes #184) (#186)
Per CLAUDE.md the Punk Exposure Engine has three verbs (Peek/Poke/
Emancipate) and Tor is one of the three exposure channels. metablogizerctl
already handled site create/delete/publish/unpublish/list (the Poke verb
at the publishing layer); this adds the Emancipate verb:

  tor expose <site>   Publish site via Tor hidden service
  tor revoke <site>   Stop publishing via Tor
  tor list            List Tor-exposed sites + onion addresses
  tor status <site>   Stanza + onion + tor service state

When secubox-exposure is installed, the verbs delegate to it for
consistency with other exposure channels (DNS+SSL, Mesh) — one
orchestrator across all three channels. When unavailable, falls back
to direct /etc/tor/secubox-metablogizer.d/<site>.conf stanza writes
and `systemctl reload tor`, reading the resulting .onion hostname from
/var/lib/tor/secubox-metablogizer/<site>/hostname.

The onion address is persisted back into the site's site.json under
exposure.tor so the Peek verb (later, separate ticket) can surface it
without re-running tor status.

Co-authored-by: CyberMind-FR <gandalf@Gk2.net>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 11:32:24 +02:00
..
api fix(metablog-webhook): pass safe.directory to git on every call (ref #117) 2026-05-13 07:43:18 +02:00
config feat(packages): Sync source packages with MOCHAbin deployed configs 2026-05-06 09:22:29 +02:00
debian feat(metablogizerctl): forge tor noun verbs — Emancipate (closes #184) (#186) 2026-05-17 11:32:24 +02:00
menu.d Add dynamic menu system with menu.d JSON definitions 2026-03-21 16:42:49 +01:00
nginx feat: modular nginx config + hub roadmap + new modules 2026-03-21 20:34:01 +01:00
sbin feat(metablogizerctl): forge tor noun verbs — Emancipate (closes #184) (#186) 2026-05-17 11:32:24 +02:00
schema feat(metablog): JSON Schema draft-07 for site.json (ref #101) 2026-05-12 17:15:14 +02:00
www/metablogizer feat(metablogizer): simplify UI to single Sites tab with rich tooltip 2026-05-14 09:19:27 +02:00
README.md docs(metablog-webhook): tracking + README deploy section (ref #113) 2026-05-13 07:32:36 +02:00

📝 Metablogizer

Static site publisher with Tor

Category: Publishing

Screenshot

Metablogizer

Features

  • Static sites
  • Tor publishing
  • Templates
  • Markdown

Gitea ingest

The 166 sites under /srv/metablogizer/sites/* are tracked in Gitea at https://gitea.gk2.secubox.in/gandalf/?tab=repositories&q=metablog as gandalf/metablog-<sitename> repositories, each with a v1.0.0 tag on the initial state.

Initial ingest is driven by three scripts in scripts/:

bash scripts/metablog-ingest-gitea-config.sh   # one-time: enables push-create
bash scripts/lib/gitea-ssh-preflight.sh --check  # verify SSH path
bash scripts/metablog-ingest.sh                # full run

Flags on metablog-ingest.sh: --dry-run, --limit N, --site <name>, --halt-on-fail.

Idempotent — re-running picks up new sites and skips ones already in sync. Per-site outcome lands in output/ingest-report.json. Important: the SSH URL uses gitea@ (NOT git@) because Gitea's built-in SSH server validates the username against the OS user it runs as.

Version metadata (site.json)

Every site under /srv/metablogizer/sites/ carries a site.json describing it. The formal schema lives at packages/secubox-metablogizer/schema/site.json.schema.json.

Required fields: name, domain, published. Optional: version, title, description, category, streamlit_app, tags, last_updated.

If version and/or last_updated are absent, the API derives them from the local git state (git describe --tags --exact-match and git log -1 --format=%cI).

The /api/v1/metablogizer/sites endpoint returns the enriched form; consumers (e.g. the upcoming sub-D dashboard) see one consistent shape.

Backfill

To create or merge site.json files in bulk:

bash scripts/metablog-site-backfill.sh --dry-run        # preview
bash scripts/metablog-site-backfill.sh                  # create missing
bash scripts/metablog-site-backfill.sh --force          # merge missing fields
bash scripts/metablog-site-backfill.sh --site <name>    # one site only

Per-run JSON report at output/metablog-backfill-report.json. The backfill auto-detects streamlit_app by probing the Gitea repo gandalf/streamlit-<name>.git (sub-F).

Version dashboard

The Hub exposes a per-site dashboard at:

  • List: https://admin.gk2.secubox.in/metablogizer/

    • 8 columns: Name · Domain · Version · Streamlit · Updated · Status · Size · Actions
    • Inline filter (matches name and domain)
    • Sortable headers (Name, Domain, Version, Updated)
    • Auto-refresh every 60s; paused when the tab is hidden
  • Per-site drill-in: https://admin.gk2.secubox.in/metablogizer/site.html?name=<sitename>

    • All site.json fields
    • Three quick links: 🌐 live site, 🦊 Gitea repo, 🎨 Streamlit app (if any)
    • Tag history is not shown inline; click the Gitea link and use its Releases tab (auth required, handled by your Gitea session)

Data comes from /api/v1/metablogizer/sites and /api/v1/metablogizer/site/<name> (sub-C, PR #102). The dashboard is pure vanilla JS — no framework, no router.

Deploy webhook

A push to the default branch of any metablog-* repo on Gitea triggers an automatic deploy of the live site.

  • Endpoint: POST https://admin.gk2.secubox.in/api/v1/metablogizer/webhook
  • Auth: HMAC-SHA256 in X-Gitea-Signature header, shared secret in /etc/secubox/metablogizer-webhook.secret (chmod 600, owner secubox)
  • Action: git fetch + git reset --hard origin/<default> under a per-site asyncio.Lock; invalidates the dashboard cache; reloads nginx only if site.json:domain changed.

Provisioning the secret (one-shot on the MOCHAbin)

sudo install -o secubox -g secubox -m 600 /dev/stdin \
  /etc/secubox/metablogizer-webhook.secret <<< "$(openssl rand -hex 32)"
sudo systemctl restart secubox-metablogizer

Registering the webhook on every Gitea repo

scripts/metablog-webhook-install.sh \
  --gitea-token <admin-or-repo-token> \
  --secret-file /etc/secubox/metablogizer-webhook.secret

The installer is idempotent — re-run after adding new sites.

Observability

  • journalctl -u secubox-metablogizer | grep '^deploy '
  • GET /api/v1/metablogizer/deploys (JWT-gated) returns the last 50 deploys

Installation

# Add SecuBox repository
curl -fsSL https://apt.secubox.in/install.sh | sudo bash

# Install package
sudo apt install secubox-metablogizer

Configuration

Configuration file: /etc/secubox/metablogizer.toml

API Endpoints

  • GET /api/v1/metablogizer/status - Module status
  • GET /api/v1/metablogizer/health - Health check

License

MIT License - CyberMind © 2024-2026