excalidraw

Hand-drawn Excalidraw JSON diagrams (arch, flow, seq).

  • Excalidraw
  • Diagrams
  • Flowcharts
  • Architecture
  • Visualization
  • JSON

Declared platforms: linux · macos · windows

Install
npx skills add 'https://github.com/NousResearch/hermes-agent/tree/main/optional-skills/creative/excalidraw'
Download bundle ↓
main · 24fd22bScanned 2026-09-15

Contributors

GitHub-linked commit authors for this SKILL.md at the saved revision. Co-authors and history before file renames are not included.

File history ↗

scripts/upload.py

scripts/upload.pyBrowse 5 files
View on GitHub
← Back to SKILL.md
#!/usr/bin/env python3"""Upload an .excalidraw file to excalidraw.com and print a shareable URL. No account required. The diagram is encrypted client-side (AES-GCM) beforeupload -- the encryption key is embedded in the URL fragment, so the servernever sees plaintext. Requirements:    pip install cryptography Usage:    python upload.py <path-to-file.excalidraw> Example:    python upload.py ~/diagrams/architecture.excalidraw    # prints: https://excalidraw.com/#json=abc123,encryptionKeyHere""" import jsonimport osimport structimport sysimport zlibimport base64import urllib.request try:    from cryptography.hazmat.primitives.ciphers.aead import AESGCMexcept ImportError:    print("Error: 'cryptography' package is required for upload.")    print("Install it with: pip install cryptography")    sys.exit(1) # Excalidraw public upload endpoint (no auth needed)UPLOAD_URL = "https://json.excalidraw.com/api/v2/post/"  def concat_buffers(*buffers: bytes) -> bytes:    """    Build the Excalidraw v2 concat-buffers binary format.     Layout: [version=1 (4B big-endian)] then for each buffer:            [length (4B big-endian)] [data bytes]    """    parts = [struct.pack(">I", 1)]  # version = 1    for buf in buffers:        parts.append(struct.pack(">I", len(buf)))        parts.append(buf)    return b"".join(parts)  def upload(excalidraw_json: str) -> str:    """    Encrypt and upload Excalidraw JSON to excalidraw.com.     Args:        excalidraw_json: The full .excalidraw file content as a string.     Returns:        Shareable URL string.    """    # 1. Inner payload: concat_buffers(file_metadata, data)    file_metadata = json.dumps({}).encode("utf-8")    data_bytes = excalidraw_json.encode("utf-8")    inner_payload = concat_buffers(file_metadata, data_bytes)     # 2. Compress with zlib    compressed = zlib.compress(inner_payload)     # 3. AES-GCM 128-bit encrypt    raw_key = os.urandom(16)   # 128-bit key    iv = os.urandom(12)        # 12-byte nonce    aesgcm = AESGCM(raw_key)    encrypted = aesgcm.encrypt(iv, compressed, None)     # 4. Encoding metadata    encoding_meta = json.dumps({        "version": 2,        "compression": "pako@1",        "encryption": "AES-GCM",    }).encode("utf-8")     # 5. Outer payload: concat_buffers(encoding_meta, iv, encrypted)    payload = concat_buffers(encoding_meta, iv, encrypted)     # 6. Upload    req = urllib.request.Request(UPLOAD_URL, data=payload, method="POST")    with urllib.request.urlopen(req, timeout=30) as resp:        if resp.status != 200:            raise RuntimeError(f"Upload failed with HTTP {resp.status}")        result = json.loads(resp.read().decode("utf-8"))     file_id = result.get("id")    if not file_id:        raise RuntimeError(f"Upload returned no file ID. Response: {result}")     # 7. Key as base64url (JWK 'k' format, no padding)    key_b64 = base64.urlsafe_b64encode(raw_key).rstrip(b"=").decode("ascii")     return f"https://excalidraw.com/#json={file_id},{key_b64}"  def main():    if len(sys.argv) < 2:        print("Usage: python upload.py <path-to-file.excalidraw>")        sys.exit(1)     file_path = sys.argv[1]     if not os.path.isfile(file_path):        print(f"Error: File not found: {file_path}")        sys.exit(1)     with open(file_path, "r", encoding="utf-8") as f:        content = f.read()     # Basic validation: should be valid JSON with an "elements" key    try:        doc = json.loads(content)    except json.JSONDecodeError as e:        print(f"Error: File is not valid JSON: {e}")        sys.exit(1)     if "elements" not in doc:        print("Warning: File does not contain an 'elements' key. Uploading anyway.")     url = upload(content)    print(url)  if __name__ == "__main__":    main() 
Referenced from SKILL.md