Skip to content

Chapter 22: Sharing Reports

Overview

melisai share turns a JSON report into a single URL you can drop into Slack, an incident channel, or a Jira ticket. The recipient opens the link in any modern browser and sees the diagnosis rendered — no melisai install, no JSON viewer, no agents.

There are two URL shapes. The CLI picks the short one by default and falls back to the long one when the backend is unreachable, so the same command works on internet-connected hosts and on airgapped servers.

melisai share demo — loaded server, health 2/100

Live demo: https://melisai.dev/r/uJOzZUjb

Two URL Shapes

Mode URL shape When it fires Storage
Short link https://melisai.dev/r/Xa9bC3kp Default. CLI POSTs to melisai.dev/api/r Backend keeps gzip blob in SQLite
Fragment fallback https://melisai.dev/r#H4sIAAAA… Upload failed (network/timeout/5xx), or --offline None — the URL is the payload

Both URLs decode to exactly the same view: meta block (hostname/kernel/profile/duration), the 0-100 health score, USE metric bars, anomalies list, recommendations with copy-paste commands.

What's in the Payload

melisai share does not upload the full report. It extracts the Summary plus a minimum metadata header:

{
  "v": 1,
  "meta": {
    "hostname":     "prod-rtb-01",
    "kernel":       "6.8.0-90-generic",
    "cpus":         32,
    "memory_gb":    128,
    "profile":      "standard",
    "duration":    "30s",
    "timestamp":    "2026-05-25T10:00:00Z",
    "tool_version": "0.6.0"
  },
  "summary": {
    "health_score":   68,
    "anomalies":     [ /* 0..N anomaly records */ ],
    "resources":     { /* USE metric per resource */ },
    "recommendations":[ /* 0..N recommendations */ ]
  }
}

The viewer page at melisai.dev/r/ only knows how to render v: 1. Bumps to the schema must keep older clients working — older code falls back to a clear "unsupported payload version" message instead of crashing.

The wire encoding is base64url(gzip(json)). Typical encoded sizes:

Profile JSON gzip+base64url
quick, healthy server ~10 KB ~1.5 KB
standard, loaded server with 15 anomalies + 10 recs ~25 KB ~3–5 KB
deep with stack traces / histograms ~80–150 KB summary still ~5–10 KB (stacks and histograms are not shipped via share)

Privacy

The payload carries identifying data: hostname, kernel version, anomaly evidence strings ("TCPRcvQDrop=1.2k/s observed across 3 interfaces"), recommendation evidence. Treat the URL with the same care as the JSON report itself.

If a hostname is sensitive, scrub it in the report before sharing — jq '.metadata.hostname = "redacted"' report.json | melisai share -.

CLI Flags

melisai share [flags] <report.json>

  --api-url     string   short-link backend (default https://melisai.dev/api/r)
  --base-url    string   viewer prefix for fragment fallback (default https://melisai.dev/r)
  --offline             skip the backend and emit a fragment URL straight away
  --timeout     duration upload budget before falling back (default 10s)

Use - instead of a path to read the report from stdin: melisai collect ... -o - | melisai share -.

Self-Hosting the Backend

The backend is the melisai-share-api binary, also in this repo:

go build ./cmd/melisai-share-api
./melisai-share-api \
  --addr=:8080 \
  --db=/var/lib/melisai-share-api/store.db \
  --public-base=https://share.example.com/r \
  --max-body-bytes=1048576 \
  --retention=2160h \
  --cleanup-interval=1h

The viewer is static HTML+JS — copy apps/melisai-site/public/r/index.html (in the hetzner-k8s-infra repo) behind any nginx that proxies /api/r to the binary above. There is no auth on POST; protect uploads with rate limiting and a firewall.

HTTP Contract

The backend exposes three endpoints. Anything else returns 404.

Method Path Body Response
POST /api/r gzip bytes (must start with 0x1f 0x8b), ≤ 1 MB 201 {"code":"Xa9bC3kp","url":"https://…/r/Xa9bC3kp"}
GET /api/r/{code} 200 raw gzip bytes (application/octet-stream, Cache-Control: public, max-age=300)
GET /healthz 200 {"status":"ok","count":N}

The viewer fetches /api/r/{code} with cache: 'no-store' so retention sweeps and takedowns aren't masked by browser cache.

Security Notes

  • Code space. 8 base62 characters = 62⁸ ≈ 2.18 × 10¹⁴ slots, generated by crypto/rand with rejection sampling. Codes are unguessable; there is no enumeration risk.
  • Body cap. The backend rejects > 1 MB and anything not starting with the gzip magic header (0x1f 0x8b).
  • Rate limit. The reference nginx config caps writes at 20/min per real client IP. Adjust to taste.
  • Schema validation. The viewer page rejects payloads whose v doesn't match SUPPORTED_VERSION.
  • XSS. Every payload field is rendered via textContent. The viewer page never feeds untrusted data to innerHTML, eval, Function, or setTimeout(string).

Source Files

File Lines Purpose
internal/share/share.go ~140 Payload schema, Encode/Decode, BuildURL
internal/share/client.go ~75 HTTP client used by the CLI for short-link upload
cmd/melisai/share.go ~100 Cobra subcommand, fragment fallback wiring
internal/shareapi/codec.go ~70 8-char base62 code generator, ValidCode
internal/shareapi/store.go ~120 SQLite store (open/put/get/count/delete-older-than)
internal/shareapi/handlers.go ~190 POST/GET/healthz handlers, body cap, collision retry
cmd/melisai-share-api/main.go ~165 Binary entry point, retention loop, graceful shutdown