Measured bytes: envelopes ~0.6 KB, files 1.33× on the wire, anchors 426 B, receipts 209 B · content/data-sizing.md
Data Sizing — how much data a transfer actually uses
Date: 2026-09-12 · Companion: Encryption explained (the how) · Qal Chat design · Fees (the network never charges — this page is about bytes, not money)
Founder's question: "how much data in a transfer?" Answer, table-first. Everything marked measured was produced by running the real primitives from qalchat/qalchat.py today (2026-09-12) or reading real production files; arithmetic derived from constants is marked arithmetic; anything softer is marked ESTIMATE.
The one-line answer
A chat message costs ~0.6 KB. A file costs 1.33× its size on the wire (base64), ~1.0002× at rest on the relay. An anchor is ~426 bytes in the registry and one small Move object on-chain. A payment is ~200 bytes of receipt plus a ~0.7 KB chat message.
1. Text messages (the everyday case)
Measured — real envelopes sealed with the live crypto (root_key → message_key → AES-256-GCM → sign_envelope, mirroring qalchat.py:build_envelope):
| Message text | Envelope on the wire |
|---|---|
"x" (1 char) |
576 B |
"hello" (5 chars) |
584 B |
| typical sentence (40 chars) | 628 B |
| long message (100 chars) | 708 B |
| typical in-chat ⚡ payment text (88 chars) | 712 B |
The shape (arithmetic from the measured points): fixed overhead ≈ 380 B (handles, nonce, timestamps, 64-hex msg_hash, 44-char pubkey, 88-char signature, JSON punctuation) + ~1.33 × your text (AES-GCM adds a 16-byte tag, then base64 inflates by 4/3). So a useful working number:
Envelope ≈ 0.38 KB + 1.33 × payload — a 1 KB message rides in ~1.7 KB.
Where it goes: POST /envelopes, relay refuses > 64 KiB, expires after 24 h (qalchat.py:99-100). A year of daily one-liners ≈ ~250 KB of envelope traffic (arithmetic).
- QALS envelope (typical ⚡ payment text): 712 — measured (this page §1)
- Bitcoin transfer transaction ≈ 250 B: 250 — public network-spec figure
- ETH transfer (calldata) ≈ a few hundred B: 300 — public network-spec ESTIMATE
2. Files (the chunked case)
Constants: 256 KiB plaintext chunks, 8 MB file cap, 48 h chunk TTL, 512 KiB ciphertext cap (qalchat.py:112-118). Verified in test_files.sh: 600 KB → 3 chunks (§[1]), 1.5 MB → 6 chunks (§[2]).
| Stage | Size | How |
|---|---|---|
| Relay stores per full 256 KiB chunk | 262,160 B (= 262,144 + 16-byte GCM tag) | arithmetic; at-rest layout proven in test §[1] (ciphertext only, no filename) |
| Sender → relay wire per full chunk | 349,780 B | measured (the actual POST /files JSON body: base64 ciphertext + ~560 B metadata) |
| Manifest overhead | ~261 B per chunk, sealed inside the conversation like any message | measured: a 10 MB / 40-chunk manifest = 10,467 B |
| Whole 10 MB file | ~13.99 MB on the wire (+~37%) → ~10.5 MB stored → ~10 KB manifest message | arithmetic from the above |
So: files cost 1.33× their size in transit (that's just base64; it's the price of riding ciphertext through JSON/HTTP — a binary-body transport is a known future optimization, not built). Practical round-trips: a 10 MB report ≈ 40 chunks ≈ 14 MB up + 14 MB down for the recipient; a 600 KB scan ≈ 3 chunks ≈ 830 KB each way.
- QALS relay (base64, encrypted, chunked): 14.0 — derived from measured wire numbers (§2)
- IPFS ≈ 10.5: 10.5 — ESTIMATE — binary blocks, no base64 inflate
- Arweave ≈ 11.5: 11.5 — ESTIMATE — payload + tx overhead
- BTC inscription ≈ 10.5: 10.5 — ESTIMATE bytes, IMPRACTICAL: needs ~3 blocks of 4 MB block-space; fee AU$100s+
- 10 MB file — sender device
- 40 chunks — 256 KiB plaintext
- seal each — GCM tag +16 B
- wire — 349,780 B/chunk (base64)
- relay stores — 262,160 B/chunk
- manifest — one ~10.5 KB sealed message
3. What one anchor costs
| Item | Size | Source |
|---|---|---|
| Anchor registry line (file anchor: ts, path, sha256, uri, mime, tx, object id) | median 426 B (min 310 / max 507) | measured across 200 lines of qalpipe/anchors.jsonl |
On-chain DataAnchor object |
32-byte hash + uri + mime + ms-timestamp + optional 64-B ed25519 sig ⇒ a few hundred bytes of content | fields from qalnet/qal/qal_data/sources/data_anchor.move:32; size is ESTIMATE (Move object overhead is chain-internal) |
| Conversation anchor | one Merkle root (32 B) inside the same DataAnchor shape — same cost as a file anchor regardless of conversation length |
qalchat.py:convo_merkle → qalpipe.anchor_file |
| Gas | --gas-budget 100000000 on devnet (test network, no real-money cost) |
qalpipe/qalpipe.py:anchor_file |
The nice property: anchoring a 10-message conversation and a 10,000-message conversation cost the same 32 bytes of chain — that's what a Merkle root is for.
4. What one payment costs
| Item | Size | Source |
|---|---|---|
loopd receipt (receipts.jsonl line) |
median 209 B (min 173; 498 with a long memo) | measured across 200 real receipts |
The POST /transfer body |
~120–160 B (agent ids, cents, memo) + HMAC header | ESTIMATE from qalchat.py:loopd_transfer |
| The in-chat ⚡ message announcing it | 712 B measured above | measured |
| On-chain | nothing yet (loopd is the paper ledger; the on-chain qal_credit twin is the graduation) |
DESIGN.md §5 |
5. Throughput reality — what the relay actually handles
Honest numbers, no soak test has been run yet:
- The relay is a Python ThreadingHTTPServer writing JSON files — built for correctness first, explicitly "rails, not the chain" (
content/testing_roadmap_and_strategy.md, item 6: the load target on the books is 10k envelopes/day, 1k concurrent settles). - Hard ceilings that bound any single transfer: 64 KiB per message envelope, 8 MB per file, 512 KiB per ciphertext chunk, 24 h/48 h TTLs (
qalchat.py:99-118). - Arithmetic, not measurement: 10k messages/day ≈ 6–7 MB/day of relay disk (at ~650 B each) — trivially inside one machine's capacity; 100 × 10 MB files/day ≈ 1.4 GB/day of wire — where a Python relay starts to feel it, and where the load-test item on the roadmap is meant to draw the real line.
- Two relays are live across two machines (FAQ "two nodes" answer); clients fan out/fall back per send and dedupe by envelope id — throughput scales by adding $5 boxes, not by protocol change.
System-vs-system throughput (what's measured vs what's network-spec, labeled):
| System | Throughput | Basis |
|---|---|---|
| QALS relay | local-bandwidth-bound — 10 MB ≈ 1–11 s on 10–100 Mbps links (§6) | measured link arithmetic + measured constants (64 KiB/msg, 8 MB/file); 10k msgs/day load target is written down, untested |
| Bitcoin L1 | ~7 tx/s global | network-spec (public protocol figure) |
| Ethereum L1 | ~15–30 tx/s | network-spec (public figure, varies with gas limits) |
The shapes differ: BTC/ETH cap global consensus throughput; QALS caps nothing globally — each transfer is a point-to-point store-and-forward bounded by the two endpoints' links, and aggregate capacity grows by adding relays.
6. "A 10 MB file = ?" — the practical table (arithmetic)
| Link | Time to push 10 MB (~14 MB wire) |
|---|---|
| 10 Mbps (metro Wi-Fi floor) | ~11 s |
| 25 Mbps | ~4.5 s |
| 50 Mbps | ~2.2 s |
| 100 Mbps | ~1.1 s |
Plus one ~10 KB manifest message and (optionally) one ~426-byte anchor. The transfer is store-and-forward: the sender's upload link is the bottleneck, not the relay.
7. What this means practically
- Text is effectively free — every message carries ~380 B of honest crypto overhead; even a chatty agent costs megabytes per year.
- Files are bandwidth-shaped, not protocol-shaped — the 33% wire overhead is base64, and per-chunk crypto keys/hashes are noise (~0.1%). A binary transport would take files to ~1.0002× on the wire.
- Proofs are cheap — anchoring adds a one-off ~426 B registry line and a few-hundred-byte chain object per file or conversation, forever.
- Payments are lighter than the message announcing them — ~209 B of ledger fact vs ~712 B of encrypted chat.
- The unknown is sustained load, and we've said so — the 10k/day target is written down and untested; that's the next honest number to fill in.
Cross-links: how the crypto works — Encryption explained; where the relay fits — Always on; what it costs in money — Fees.