{"id":49085437,"url":"https://github.com/calibrae/capsaicin","last_synced_at":"2026-04-20T15:10:31.898Z","repository":{"id":351093911,"uuid":"1209549760","full_name":"calibrae/capsaicin","owner":"calibrae","description":"Pure-Rust SPICE remote-display protocol implementation. Client + server + standalone QUIC/LZ/GLZ image decoders.","archived":false,"fork":false,"pushed_at":"2026-04-13T15:14:22.000Z","size":151,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-13T16:28:57.015Z","etag":null,"topics":["image-codec","kvm","libvirt","qemu","remote-display","rust","spice","spice-protocol","virtualization","vm"],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/calibrae.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE-APACHE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-13T14:38:51.000Z","updated_at":"2026-04-13T15:14:26.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/calibrae/capsaicin","commit_stats":null,"previous_names":["calibrae/capsaicin"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/calibrae/capsaicin","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/calibrae%2Fcapsaicin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/calibrae%2Fcapsaicin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/calibrae%2Fcapsaicin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/calibrae%2Fcapsaicin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/calibrae","download_url":"https://codeload.github.com/calibrae/capsaicin/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/calibrae%2Fcapsaicin/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32052684,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-20T11:35:06.609Z","status":"ssl_error","status_checked_at":"2026-04-20T11:34:48.899Z","response_time":94,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["image-codec","kvm","libvirt","qemu","remote-display","rust","spice","spice-protocol","virtualization","vm"],"created_at":"2026-04-20T15:10:30.751Z","updated_at":"2026-04-20T15:10:31.882Z","avatar_url":"https://github.com/calibrae.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Capsaicin 🌶️\n\nA pure-Rust [SPICE](https://www.spice-space.org/) remote-display protocol\nimplementation. Both client and server. No C bindings, no libspice, no\nglib.\n\nConnects to real QEMU SPICE servers, decodes a live KDE desktop, and\nforwards keyboard + mouse back over the inputs channel.\n\n## Status\n\nEarly but working. The viewer renders a real VM at 1280×800 with input\nforwarding. See [JOURNEY.md](JOURNEY.md) for the build log and what's\ndone vs what isn't.\n\n```\n97 tests passing across the workspace\n```\n\n## Workspace\n\n```\ncrates/\n├── capsaicin-proto/   wire format: link, channels, message bodies\n├── capsaicin-net/     tokio link handshake (client + server) + framing\n├── capsaicin-quic/    standalone QUIC image decoder\n├── capsaicin-lz/      standalone SPICE LZ decoder\n├── capsaicin-glz/     standalone GLZ decoder + per-session image dictionary\n├── capsaicin-server/  embeddable server core (link accept + main bootstrap)\n├── capsaicin-client/  event-driven client (SpiceClient::connect → next_event loop)\n└── capsaicin-cli/     `capsaicin` binary: connect / serve / view subcommands\n```\n\nThe codec crates (`capsaicin-quic`, `capsaicin-lz`, `capsaicin-glz`)\nare deliberately standalone — they only depend on `std` + `thiserror`\n(plus `capsaicin-lz` for `LzImageType` in `capsaicin-glz`). Useful as\nself-contained \"decode SPICE-compressed images\" libraries beyond the\ncontext of capsaicin itself.\n\n## Try it\n\n```bash\ncargo build --release\n\n# Connect to a SPICE server, log what's happening (no rendering)\nRUST_LOG=capsaicin=info ./target/release/capsaicin connect 127.0.0.1:5900\n\n# Live viewer with window, keyboard, mouse\n./target/release/capsaicin view 127.0.0.1:5900\n\n# Serve as a SPICE endpoint (mostly for testing — no framebuffer source yet)\n./target/release/capsaicin serve 127.0.0.1:5900 --password sesame\n```\n\nFor a SPICE-enabled QEMU VM, the typical incantation is:\n```\n-spice port=5900,addr=127.0.0.1,disable-ticketing=on\n```\n\nIf the VM lives on a remote host, tunnel first:\n```\nssh -L 5900:127.0.0.1:5900 remote-host\n```\n\n## Use as a library\n\n```rust\nuse capsaicin_client::{SpiceClient, ClientEvent, DisplayEvent, InputEvent, RegionPixels};\n\nlet mut client = SpiceClient::connect(\"127.0.0.1:5900\", \"\").await?;\nclient.send_input(InputEvent::KeyDown(0x1e)).await?;  // press 'A'\n\nwhile let Some(evt) = client.next_event().await {\n    match evt {\n        ClientEvent::Display(DisplayEvent::SurfaceCreated { width, height, .. }) =\u003e { /* allocate */ }\n        ClientEvent::Display(DisplayEvent::Region { rect, pixels: RegionPixels::Raw { data, stride }, .. }) =\u003e {\n            // blit BGRA pixels at rect\n        }\n        ClientEvent::Display(DisplayEvent::CopyRect { src_x, src_y, dest_rect, .. }) =\u003e {\n            // copy rect within framebuffer\n        }\n        ClientEvent::Closed(_) =\u003e break,\n        _ =\u003e {}\n    }\n}\n```\n\n## What works\n\n- Plain TCP + RSA-OAEP ticket auth\n- Main / Display / Inputs channels with proper `MSGC_ACK` flow control\n- Pixel decoding: RAW BITMAP, LZ_RGB, GLZ_RGB (cross-image), QUIC\n  (RGB32/24/Rgba/RGB16/Gray), MJPEG via streams\n- `COPY_BITS` for compositor rect-copy operations\n- `DRAW_FILL` solid color\n- Keyboard + mouse with proper PC AT scancodes (incl. extended `0xE0`\n  prefix and `0x80` break-bit on release)\n\n## What doesn't (yet)\n\n- TLS — most production deployments require it\n- Cursor channel — visible flicker around the cursor\n- Agent channel (clipboard, dynamic resolution)\n- Audio (Playback / Record)\n- USB redirect, smartcard, webdav, port channels\n- Image types: JPEG, JPEG_ALPHA, ZLIB_GLZ_RGB, LZ4\n- Stream codecs beyond MJPEG: VP8 / VP9 / H.264 / H.265\n- Full draw command set (DRAW_OPAQUE, DRAW_BLEND, DRAW_ROP3, etc.)\n\n[JOURNEY.md](JOURNEY.md) has the full picture plus a roadmap of what\nto tackle next.\n\n## Related\n\n- [`spice-client`](https://crates.io/crates/spice-client) — a parallel\n  pure-Rust SPICE client effort by `arsfeld` as part of\n  [quickemu-manager](https://github.com/arsfeld/quickemu-manager). It\n  has cursor + WASM but no QUIC/GLZ/LZ_RGB body decode at time of\n  writing, and ships with mismatched MIT/GPL-3.0 metadata. Independent\n  reimplementation felt cleaner.\n- [SPICE protocol spec](https://www.spice-space.org/spice-protocol.html)\n- [`spice-common`](https://gitlab.freedesktop.org/spice/spice-common) —\n  reference C implementation we ported from\n- [`spice-gtk`](https://gitlab.freedesktop.org/spice/spice-gtk) — the\n  reference GTK client; we ported its GLZ decoder\n\n## License\n\nDual-licensed under either of\n\n- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE))\n- MIT license ([LICENSE-MIT](LICENSE-MIT))\n\nat your option.\n\n## Contributing\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcalibrae%2Fcapsaicin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcalibrae%2Fcapsaicin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcalibrae%2Fcapsaicin/lists"}