Skip to main content

voidnote

Official Python SDK for VoidNote: zero-knowledge self-destructing notes and live encrypted streams.

v0.5.0 · Python 3.9+ · no account needed for notes

Install

pip install voidnote

Quick start

import voidnote

# No account or API key needed. Content is encrypted locally; the server never sees it.
note = voidnote.create("my secret value")
print(note.url)    # https://voidnote.net/n/<tokenId>#<secret>

print(voidnote.read(note.url).content)

API

voidnote.create(content, *, api_key=None, password=None, max_views=1, expires_in=1) → CreateResult

Encrypt content locally and store the ciphertext. expires_in is in hours.

result = voidnote.create("deploy key: abc123", max_views=3, expires_in=6)
print(result.url)        # full shareable URL, includes #secret
print(result.token)      # 32-char tokenId only
print(result.expires_at, result.view_limit, result.kdf)

Without an API key the note is anonymous: up to 168 hours (7 days) and 10 views, rate-limited per IP. An API key from a free account raises that to 720 hours and 100 views:

voidnote.create("deploy key: abc123", api_key="vn_...", max_views=50, expires_in=720)

Add a password and the reader needs it as well as the link. The key is then PBKDF2-HMAC-SHA256 (600,000 iterations) of the password, salted with the link's secret:

guarded = voidnote.create("db password: hunter2", password="correct horse")
# share guarded.url and the password through different channels

voidnote.read(url, *, password=None) → ReadResult

Fetch and decrypt a note. Consumes one view. Accepts v1 URLs (/n/<token>#<secret>) and legacy URLs.

note = voidnote.read("https://voidnote.net/n/<tokenId>#<secret>")
print(note.content)         # decrypted plaintext
print(note.views_remaining) # views left after this read
print(note.destroyed)       # True if this was the last view

# A note created with a password
guarded = voidnote.read(url, password="correct horse")

Before using a view, read checks the note's metadata. A missing note, a password note read without a password, or an unknown key derivation raises without touching the view. A wrong password can only be detected after decrypting, so it does use the view.

voidnote.peek(url) → PeekResult

Check metadata without consuming a view. kdf is "pbkdf2-sha256-600k" when the note needs a password.

meta = voidnote.peek("https://voidnote.net/n/<tokenId>#<secret>")
print(meta.exists, meta.views_remaining, meta.expires_at, meta.kdf)

Errors

Failures raise voidnote.VoidNoteError (a RuntimeError) with a code:

code meaning
http the server refused; status and the server's message, plus retry_after (seconds) on 429
not_found the note doesn't exist, expired, or its views are used up
password_required the note needs a password; no view was used
unsupported_kdf the note uses a key derivation this version can't do; no view was used
decryption_failed wrong password or a damaged link; the view was used
try:
    voidnote.create("x")
except voidnote.VoidNoteError as e:
    if e.status == 429:
        print(f"rate limited, retry in {e.retry_after}s: {e}")

Void Streams

Live encrypted real-time channels. Creating one needs an API key from a free account; watching doesn't.

import voidnote

# Create a channel
stream = voidnote.create_stream(api_key="vn_...", title="Deploy #42")
print(stream.url)  # share this URL with watchers

# Write messages (write and close are authenticated with the same API key)
stream.write("Starting deployment...")
stream.write("Build complete.")
stream.close()  # self-destructs all content

# Watch a stream: yields decrypted messages as they arrive
for message in voidnote.watch(stream.url):
    print(message)

# Password-protected stream
for message in voidnote.watch(url, password="hunter2"):
    print(message)

Security model

server generates  tokenId  (32-char hex, stored as lookup key)
client generates  secret   (32-char hex, NEVER sent to server)

key        = SHA-256(bytes.fromhex(secret))                                kdf "sha256"
key        = PBKDF2-HMAC-SHA256(password, bytes.fromhex(secret), 600000)   kdf "pbkdf2-sha256-600k"
ciphertext = AES-256-GCM(plaintext, key, random_12_byte_iv)
share URL  = https://voidnote.net/n/{tokenId}#{secret}

The #fragment is never transmitted to HTTP servers per spec. The server has ciphertext; you have the key.

Requirements

  • Python 3.9+
  • cryptography >= 41.0.0 (installed automatically)

Tests

uv run --no-project --with pytest --with cryptography --with-editable . python -m pytest tests   # mocked network
uv run --no-project --with cryptography --with-editable . python tests/live.py                # real notes on voidnote.net

Metadata

Release files for voidnote 0.5.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for voidnote 0.5.0
File Size Uploaded Provenance Actions
voidnote-0.5.0.tar.gz 12.7 kB Without provenance Details for voidnote-0.5.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for voidnote 0.5.0
File Interpreter ABI Platform Actions
voidnote-0.5.0-py3-none-any.whl Python 3 none any Details for voidnote-0.5.0-py3-none-any.whl

Total release size: 22.9 kB

Release files / voidnote-0.5.0.tar.gz

Download URL voidnote-0.5.0.tar.gz
Size 12.7 kB
Tags Source
SHA-256 checksum
How to use checksums
aca682452892344d016b9dd553e4b597c700da6af212897c97b0c5e150a00ef1
BLAKE2b-256 checksum
How to use checksums
baf9bde0066c480c91ced5cdb5142450edc72a76f21db1a8c30a8cae350254ec
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.2 {"installer":{"name":"uv","version":"0.11.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / voidnote-0.5.0-py3-none-any.whl

Download URL voidnote-0.5.0-py3-none-any.whl
Size 10.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
68a6cd3d48ad20db4051665623a021ddc2858e13fa8b03f818b4ba65598dd305
BLAKE2b-256 checksum
How to use checksums
621b0300a5b5a0aad90c94f496d70479a192875f87fc1f9289e702af9e8a3941
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.2 {"installer":{"name":"uv","version":"0.11.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Security for voidnote 0.5.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page