Status: Informational (and Chaotic) Category: Experimental Protocol Abuse Author: G. Fawcett Date: December 2024
This document specifies HONK (Hierarchical Object Namespace for Knowledge), a distributed content-addressed storage system implemented entirely within the existing Domain Name System (DNS) infrastructure. HONK repurposes DNS record types according to their actual capabilities rather than their intended uses, providing a globally distributed, cached, reactive data store at the cost of one domain registration ($12/year USD).
The key insight is that DNS has always been a filesystem. This document merely acknowledges that fact.
This memo describes a protocol that makes DNS administrators uncomfortable. Distribution of this memo is unlimited, ideally via DNS TXT records.
The Domain Name System [RFC1035] was designed to map human-readable names to network addresses. Over four decades of deployment have revealed that DNS is, in fact, a globally distributed, hierarchically namespaced, typed, cached key-value store with built-in replication, aliasing, priority ordering, and arbitrary payload storage.
HONK acknowledges this reality and provides a formal specification for using DNS as a general-purpose distributed storage system.
The author's daughter coughed directly into his face, resulting in a sick day. The author had a half-hour drive. These conditions produced HONK.
The key words "MUST", "SHOULD", "HONK", and "REALLY SHOULD HAVE KNOWN BETTER" in this document are to be interpreted as the author sees fit.
Chunk: A unit of content stored in a TXT record, maximum 255 bytes.
Chunk Pool: The global namespace of numbered TXT records within a domain.
Coordinate: A tuple of (start, count, flags) addressing a range within the chunk pool.
Manifest: An SRV record specifying coordinates and capabilities for a resource.
Pointer: A CNAME record providing indirection to another name.
Goose: The chaotic waterfowl energy that drives this protocol.
HONK: Both the protocol name and the sound of approval.
Queue: A timestamped sequence of TXT records in the .q namespace, ordered by millisecond timestamp.
Offset: A counter tracking the last-read position in a queue, enabling at-least-once delivery without deletes.
Counter: A CRDT (Conflict-free Replicated Data Type) implemented via AAAA records, where each node maintains its own value and readers sum all nodes.
Node ID: A 4-character identifier for a HONK client, used to partition counter writes and queue offsets.
Objects are defined by their morphisms, not their labels. A DNS record is what it can do, not what the RFC says it is "for."
Every protocol specification describes one possible use of the underlying capability. HONK describes another.
The protocol designers built flexibility into DNS. HONK merely uses it.
The Yoneda Lemma states: an object X is completely determined by the collection of all morphisms pointing into it from every other object.
You don't need to look inside X. You only need to know how everything else relates to X.
Applied to DNS: A record type is not defined by what RFC 1035 says it's "for." It's defined by the operations you can perform on it:
| Record | Morphisms (What You Can Do) | Therefore It Is |
|---|---|---|
| TXT | Read arbitrary bytes, write arbitrary bytes | A storage cell |
| SRV | Query structured tuple, route based on fields | A manifest/coordinate |
| CNAME | Resolve to another name, chain resolutions | A pointer/reference |
| MX | Query prioritized list, failover semantics | A priority queue |
| A/AAAA | Resolve to numeric value, multiple per name | A numeric array |
The arrows (morphisms) define the object. DNS records ARE what you can do with them.
Two systems are isomorphic when they admit the same morphisms—when the set of valid transformations applicable to one equals those applicable to the other.
Claim: DNS ≅ Filesystem
| Filesystem Morphism | DNS Morphism |
|---|---|
read(path) |
dig TXT name |
write(path, data) |
API: create TXT record |
stat(path) |
dig SRV name (metadata) |
readlink(path) |
dig CNAME name |
readdir(path) |
dig ANY name |
chmod(path, mode) |
Update SRV port field (capabilities) |
| Cache invalidation | TTL expiry |
| Symlink | CNAME |
| Hardlink | Multiple records pointing to same chunk |
If Hom(X, DNS) ≅ Hom(X, Filesystem) for all relevant X, then DNS ≅ Filesystem.
The isomorphism is not metaphorical. The operations are the same. Therefore the systems are the same.
In HONK, each DNS query is a morphism—a transformation of context:
query("latest.blog.loss.dev")
→ CNAME morphism → "v3.blog.loss.dev"
→ SRV morphism → (46, 26, 1, "loss.dev")
→ TXT morphisms × 26 → [chunk₀, chunk₁, ..., chunk₂₅]
→ base64 morphism → compressed_bytes
→ gunzip morphism → content
The resolution chain IS a middleware pipeline. Each record type is a transformation. The content emerges from the composition of morphisms.
Data at rest tells you nothing. Data in motion tells you everything.
A TXT record sitting in DNS has no semantics until something queries it, decodes it, decompresses it, acts on it. Each transformation is a morphism. The collection of morphisms defines the meaning.
The functor is constructive: This isn't abstract proof. The code exists. The DNS records exist. Run the functor yourself and get a pelican out:
honk get pelicans --domain=loss.dev
# Output: pelicans.out (27KB JPEG of pelicans)
The RFC-as-thesis proves categorical structure by instantiation. Here's the functor. Here's identity. Here's composition. Execute them.
HONK is homoiconic—code, data, and metadata share the same representation:
The system interprets itself. The format that stores content also stores the instructions for decoding that content.
In category theory, every object has an identity morphism—an arrow from itself to itself that does nothing.
In HONK, the identity arrow is:
dig +short TXT peak.loss.dev | tr -d '"' | bash
Output:
| ||
| |_
The system, queried about itself, returns Loss. The identity arrow is a shitpost. This is appropriate.
🍋
HONK assigns the following semantics to DNS record types:
| Record | Traditional Use | HONK Semantics |
|---|---|---|
| TXT | Human-readable notes | Content chunks (255 bytes each) |
| SRV | Service location | Manifest/coordinates (start, count, flags, pool) |
| CNAME | Hostname alias | Pointer/reference (indirection, versioning) |
| MX | Mail routing | Priority queue (version failover chain) |
| A | IPv4 address | 32-bit value / 4-byte self-ordering chunk |
| AAAA | IPv6 address | 128-bit value / 16-byte self-ordering chunk |
The true power of AAAA records: each segment is 16 bits. UTF-16 code units are 16 bits. AAAA records ARE Unicode strings.
AAAA = 8 segments × 16 bits = 8 UTF-16 code units
= 1 sequence + 7 characters (BMP)
= 1 sequence + up to 3 surrogate pairs + leftovers
honk.loss.dev. AAAA 0001:0048:004f:004e:004b:d83e:debf:2764
seq H O N K 🪿hi 🪿lo ❤
Output: HONK🪿❤
The goose emoji (U+1FABF) requires a surrogate pair:
Four ASCII characters + one emoji (2 code units) + one BMP heart = exactly 7 code units. Perfect fit.
honk unicode-get honk --domain=loss.dev
# Output: HONK🪿❤
The CLI handles IPv6 normalization (DNS strips leading zeros, compresses ::) and UTF-16 surrogate pair decoding automatically.
Bashō's frog haiku (古池や蛙飛び込む水の音) stored as native UTF-16:
haiku.loss.dev. AAAA 0001:53e4:6c60:3084:86d9:98db:3073:8fbc
haiku.loss.dev. AAAA 0002:3080:6c34:306e:97f3:0000:0000:0000
honk unicode-get haiku --domain=loss.dev
# Output: 古池や蛙飛び込む水の音
Sort by first segment, concatenate segments 2-8, interpret as UTF-16, strip nulls. No base64. No chunking. Just poetry, cached globally, resolved in milliseconds.
TXT records store content chunks. Each record holds up to 255 bytes of payload, typically base64-encoded.
_42.pool.example.com. TXT "SGVsbG8gV29ybGQh..."
SRV records specify coordinates into the chunk pool:
_resource._tcp.example.com. SRV <start> <count> <flags> <pool-domain>
Fields:
A TXT record at the same name as the SRV manifest provides extended metadata:
_resource._tcp.example.com. SRV 0 146 1 pool.example.com.
_resource._tcp.example.com. TXT "mime=image/jpeg;size=27282;name=pelican.jpg"
Format: Semicolon-separated key=value pairs (like SPF/DKIM). Simple, human-readable, no parsing libraries.
Standard keys:
| Key | Description | Example |
|---|---|---|
mime |
MIME type | image/jpeg, text/plain, application/x-sh |
size |
Original size in bytes | 27282 |
name |
Original filename | pelican.jpg |
enc |
Encoding if not base64 | raw, utf16 |
created |
Unix timestamp | 1734400000 |
sha256 |
Content hash (first 16 chars) | a3f2b1c4d5e6f7g8 |
Keys are case-insensitive. Unknown keys SHOULD be ignored. Order is not significant.
The metadata record shares the SRV's TTL and lifecycle—when you create a manifest, create its metadata; when you delete a manifest, delete its metadata.
CNAME records provide indirection, enabling versioning and reactivity:
latest.blog.example.com. CNAME v3.blog.example.com.
MX records specify version precedence for failover:
blog.example.com. MX 10 v3.blog.example.com.
blog.example.com. MX 20 v2.blog.example.com.
blog.example.com. MX 30 v1.blog.example.com.
A records encode 4 bytes as dotted-quad notation. The first octet specifies sequence order:
hello.example.com. A 01.72.101.108 ; seq=1, "Hel"
hello.example.com. A 02.108.111.32 ; seq=2, "lo "
hello.example.com. A 03.87.111.114 ; seq=3, "Wor"
hello.example.com. A 04.108.100.33 ; seq=4, "ld!"
Decoding: Sort by first octet, concatenate remaining octets, interpret as ASCII.
AAAA records encode 16 bytes in IPv6 notation. First 16 bits specify sequence:
data.example.com. AAAA 0001:4865:6c6c:6f20:576f:726c:6421:0000
Decoding: Sort by first segment, concatenate remaining segments, decode as binary.
HONK supports the following data types:
| Type | Implementation | Max Size |
|---|---|---|
| string | TXT record | 255 bytes |
| blob | Multiple TXT chunks | Unlimited |
| int32 | A record (as dotted quad) | 32 bits |
| int128 | AAAA record | 128 bits |
| list | Multiple A/AAAA/TXT records | Unlimited |
| sortedlist | MX records (priority-ordered) | Unlimited |
| manifest | SRV record | 48 bits + hostname |
| ref | CNAME record | Pointer to name |
| queue | Timestamped TXT in .q namespace | Unlimited (TTL-bounded) |
| counter | AAAA CRDT (per-node values) | 16-bit per node |
| offset | TXT record (per-node queue read position) | 48-bit ms timestamp |
| kv | SRV version + TXT value | 255 bytes |
| chain | CNAME chain (delta encoding) | Bounded by chain depth |
| template | CNAME chain referencing codebook paper | Bounded by chain depth |
Every resource in HONK is addressed by coordinates:
(start, count, flags, universe)
Where:
This provides 48 bits of address space per domain, sufficient for addressing 2^16 chunks of 255 bytes each (~16MB) per namespace.
The coordinate system may be interpreted spatially:
(x, y, z) = (start, count, flags)
CNAME records function as wormholes, teleporting resolution to different coordinates.
The SRV port field is interpreted as a 16-bit capability bitfield.
Implementation Note: DNS providers require port ≥ 1. Bit 0 is therefore the HONK marker, always set to 1 for valid HONK resources. This guarantees all HONK ports are ≥ 1.
Bit 0 (0x0001): HONK - Protocol marker (MUST be set)
Bit 1 (0x0002): GZIP - Content is gzip compressed
Bit 2 (0x0004): URL - Content is a URL (dereference, don't display)
Bit 3 (0x0008): EXEC - Content is executable (pipe to shell)
Bit 4 (0x0010): BINARY - Content is base64-encoded binary
Bit 5 (0x0020): IMAGE - Content is an image
Bit 6 (0x0040): UTF16 - Content is UTF-16 encoded
Bit 7 (0x0080): ENCRYPTED - Content is encrypted
Bit 8 (0x0100): SIGNED - Content has signature (in separate record)
Bit 9 (0x0200): CONTINUES - Resource continues at target hostname
Bit 10 (0x0400): STREAMING - Append-only, check for new chunks
Bit 11 (0x0800): IMMUTABLE - Content will never change (cache forever)
Bits 12-15: Reserved for future chaos
| Flags | Hex | Meaning |
|---|---|---|
| 1 | 0x0001 | Raw text (HONK only) |
| 3 | 0x0003 | Gzipped text |
| 11 | 0x000B | Gzipped executable |
| 19 | 0x0013 | Gzipped binary |
| 27 | 0x001B | Gzipped binary executable |
| 5 | 0x0005 | URL to dereference |
| 17 | 0x0011 | Binary (no compression) |
| 33 | 0x0021 | Image (no compression) |
| 35 | 0x0023 | Gzipped image |
Implementations SHOULD auto-detect capability flags from file type:
| Extension | MIME Type | Suggested Flags | Hex |
|---|---|---|---|
.txt |
text/plain | HONK | 0x0001 |
.md |
text/markdown | HONK + GZIP | 0x0003 |
.html |
text/html | HONK + GZIP | 0x0003 |
.json |
application/json | HONK + GZIP | 0x0003 |
.sh |
application/x-sh | HONK + GZIP + EXEC | 0x000B |
.png |
image/png | HONK + BINARY + IMAGE | 0x0031 |
.jpg |
image/jpeg | HONK + BINARY + IMAGE | 0x0031 |
.gif |
image/gif | HONK + BINARY + IMAGE | 0x0031 |
.gz |
application/gzip | HONK + BINARY | 0x0011 |
.tgz |
application/gzip | HONK + BINARY | 0x0011 |
.zip |
application/zip | HONK + BINARY | 0x0011 |
.bin |
application/octet-stream | HONK + BINARY | 0x0011 |
.wasm |
application/wasm | HONK + BINARY | 0x0011 |
For already-compressed formats (.gz, .tgz, .zip, .jpg), do NOT set GZIP—double compression wastes space.
Implementations MAY prompt for confirmation: "Detected image/jpeg → flags 0x0031 (HONK+BINARY+IMAGE). Confirm? [Y/n]"
HONK achieves reactivity through TTL layering:
| Layer | Record Type | Recommended TTL | Mutability |
|---|---|---|---|
| Pointers | CNAME | 60 seconds | Mutable (flip to update) |
| Manifests | SRV | 3600 seconds | Semi-stable |
| Content | TXT | 86400 seconds | Immutable |
To update a resource:
Within one pointer TTL (60 seconds), all resolvers worldwide will see the new version. Content remains cached; only pointer resolution is repeated.
To rollback: Update CNAME to point to previous manifest. Old chunks remain in pool.
A HONK domain maintains a global chunk pool:
_1.pool.example.com. TXT "..."
_2.pool.example.com. TXT "..."
_3.pool.example.com. TXT "..."
...
_N.pool.example.com. TXT "..."
Multiple resources share the same pool. SRV manifests are views into this pool.
Chunks SHOULD be named with underscore prefix and numeric index:
_<index>.<namespace>.<domain>
HONK does not specify garbage collection. Chunks are cheap. Domains are $12/year. Let them accumulate.
Information is carried in the differences between consecutive CNAME node names rather than in any record value. Each chain node is named n<NNNN>.<chain>.<domain> where NNNN is a monotonically increasing integer. The arithmetic difference between consecutive integers is the ASCII codepoint of one character of the encoded message.
Records:
<chain>.<domain> CNAME n0001.<chain>.<domain>.
n0001.<chain>.<domain> CNAME n<N1>.<chain>.<domain>.
n<N1>.<chain>.<domain> CNAME n<N2>.<chain>.<domain>.
...
The leading <chain>.<domain> CNAME is the entry point and points at the first node. The first node anchors the chain at an arbitrary base value.
Encoding:
Decoding:
Record values do not carry any of the message — only the names do, and only as differences. A dig CNAME on the chain reveals only the structure.
honk steg preview "hello"
honk steg encode "hello" --chain=greeting
honk steg decode greeting
Whisper composes two CNAME chains and one TXT record into a remote-procedure invocation. The substrate is the same CNAME-delta encoding from Section 11.8 with two additions: reserved node numbers and an external codebook.
A whisper message at id <msg_id> occupies three names under <msg_id>.whisper.<domain>:
template.<msg_id>.whisper.<domain> — entry point of the template chainvalues.<msg_id>.whisper.<domain> — entry point of the values chainop.<msg_id>.whisper.<domain> — TXT record carrying the operation verb (GET, PUT, or EXEC)| Node | Meaning |
|---|---|
n0000 |
HOLE marker (template chain only) — a value from the values chain binds here |
n0001 |
Operation: GET (first node of template chain) |
n0002 |
Operation: PUT |
n0003 |
Operation: EXEC |
The template encodes a structure of text fragments and HOLE markers. The first node identifies the operation. Each text fragment is encoded as a pair of nodes whose values are the (start, end) byte offsets into a frozen codebook paper (Section 11.9.5). Each HOLE is encoded as n0000.
The values chain uses the STEG delta encoding (Section 11.8) to carry runtime values, with byte 0x01 (SOH) separating successive values.
Literal text in templates is referenced by coordinate into a paper hosted on Zenodo. The DOI is the source of truth; clients cache extracted text locally at ~/.cache/honk/papers/.
| ID | Name | Zenodo |
|---|---|---|
| 0 | SyneState | 18180051 |
| 1 | Wanderland | 18181233 |
A template literal that does not appear in the paper falls back to a raw-bytes encoding mode; this is less compact and is treated as a degraded path.
The receiver performs four phases:
honk whisper papers
honk whisper fetch-papers
honk whisper preview "Deploy {HOLE} to {HOLE}" nginx prod
honk whisper send msg001 "Deploy {HOLE} to {HOLE}" nginx prod
honk whisper recv msg001
EXEC operations execute on the receiver. Treat them with the same care as Section 13.1.
The simple way:
honk get pelicans --domain=loss.dev
# Output: pelicans.out (27KB JPEG)
honk get blog --domain=loss.dev
# Output: blog.out (markdown content)
The CLI handles SRV manifest lookup, chunk fetching, base64 decoding, and gzip decompression automatically.
For the curious (or those implementing their own client):
# Get manifest
srv=$(dig +short SRV _resource._tcp.example.com)
start=$(echo $srv | awk '{print $1}')
count=$(echo $srv | awk '{print $2}')
flags=$(echo $srv | awk '{print $3}')
pool=$(echo $srv | awk '{print $4}')
# Fetch chunks
for i in $(seq $start $((start+count-1))); do
dig +short TXT _$i.$pool | tr -d '"'
done | base64 -d | {
# Decompress if GZIP flag set
[[ $((flags & 2)) -ne 0 ]] && gunzip || cat
}
# Encode content
cat content.txt | gzip | base64 > encoded.txt
# Split into chunks
split -b 250 encoded.txt chunk_
# Upload chunks (implementation-specific)
# Create SRV manifest pointing to chunk range
# Update CNAME to flip pointer
# (DNS provider API specific)
Queue items are timestamped TXT records in the .q namespace:
_<timestamp>.<queue>.q.<domain> TXT "<payload>"
Timestamp is milliseconds since epoch, providing natural ordering.
# Enqueue a job
honk enqueue jobs '{"task":"build","env":"prod"}'
# Creates: _1766099487050.jobs.q.loss.dev TXT '{"task":"build","env":"prod"}'
Dequeue uses an offset as consumer position pattern. Each consumer (identified by node ID) maintains its own read position via TXT record:
_<node>.jobs.offset.<domain> TXT "<timestamp>"
Algorithm:
# Dequeue oldest unread
honk dequeue jobs
# Returns: {"task":"build","env":"prod"}
# Updates offset to 1766099487050
# Dequeue again
honk dequeue jobs
# Returns next item, or "Queue jobs is empty" if caught up
Key insight: No deletes required. Queue items remain until TTL expires. The offset tracks consumption. Multiple consumers each maintain their own offset (like Kafka consumer groups).
Peek mode: Use --peek to read without updating offset.
Counters use AAAA records with CRDT semantics. Each node publishes its own increment; readers sum all nodes:
<counter>.c.<domain> AAAA <node>:<value>:0000:<time>:0000:0000:0000:0000
Format breakdown:
# Increment counter
honk incr page_views
# Read counter total
honk counter page_views
# Output: 42
# Read with breakdown by node
honk counter page_views --verbose
# Output: page_views = 42 (node1: 15, node2: 27)
# Read total (sum all nodes)
dig +short AAAA page_views.c.loss.dev | awk -F: '{sum += strtonum("0x"$2)} END {print sum}'
CRDT property: Multiple writers never conflict. Each node only updates its own record. Readers merge by summing. Convergence is guaranteed.
Key-value storage uses SRV records for versioning:
_<key>._kv.<domain> SRV <version> 0 0 <value-encoding>
_<key>._kv.<domain> TXT "<value>"
# Set value
honk set rate_limit 100
# Compare-and-swap (only succeeds if version matches)
honk cas rate_limit 150 --version=3
; Pointer (reactive)
latest.blog.example.com. CNAME v3.blog.example.com.
; Manifest (semi-stable)
v3.blog.example.com. SRV 146 26 1 pool.example.com.
; Content (immutable)
_146.pool.example.com. TXT "H4sIAAAAAAAA..."
_147.pool.example.com. TXT "..."
...
_171.pool.example.com. TXT "..."
Retrieval:
dig +short TXT blog.example.com | tr -d '"' | bash
; Installer
rss.example.com. TXT "dig +short SRV _rss._tcp.example.com | awk '{gsub(/\\.$/,\"\",$4); printf \"https://%s/%c%c\\n\", $4, int($3/256), $3%256}'"
; Manifest with port-encoded path
_rss._tcp.example.com. SRV 0 0 26729 graemefawcett.ca.
Port 26729 = 0x6869 = "hi" (ASCII)
Retrieval:
dig +short TXT rss.example.com | tr -d '"' | bash
# Output: https://graemefawcett.ca/hi
hello.example.com. A 01.72.101.108
hello.example.com. A 02.108.111.32
hello.example.com. A 03.87.111.114
hello.example.com. A 04.108.100.33
Retrieval:
dig +short A hello.example.com | sort -t. -k1 -n | cut -d. -f2-4 | while read a b c; do printf "\\x$(printf '%02x' $a)\\x$(printf '%02x' $b)\\x$(printf '%02x' $c)"; done
# Output: Hello World!
Flag 0x0004 (EXEC) instructs clients to pipe content to a shell. This is as dangerous as it sounds. Users SHOULD only execute HONK content from trusted domains.
HONK content SHOULD be served from DNSSEC-signed zones to prevent tampering.
DNS providers implement rate limiting. High-frequency HONK operations may be throttled.
Standard DNS cache poisoning attacks apply. See [RFC5452].
If you encounter a goose while implementing HONK:
This document requires no IANA actions. HONK uses existing DNS record types for purposes their designers probably anticipated but hoped nobody would actually attempt.
Mockapetris, P. (1987). Domain Names - Implementation and Specification. RFC 1035. https://doi.org/10.17487/RFC1035
Gulbrandsen, A., Vixie, P., & Esibov, L. (2000). A DNS RR for specifying the location of services (DNS SRV). RFC 2782. https://doi.org/10.17487/RFC2782
Kitterman, S. (2014). Sender Policy Framework (SPF). RFC 7208. https://doi.org/10.17487/RFC7208
Masinter, L. (1998). Hyper Text Coffee Pot Control Protocol (HTCPCP/1.0). RFC 2324. https://doi.org/10.17487/RFC2324
Hubert, A. & van Mook, R. (2009). Measures for Making DNS More Resilient against Forged Answers. RFC 5452. https://doi.org/10.17487/RFC5452
CAD. (2008). Loss. Ctrl+Alt+Del. https://is.this.loss.dev
Yoneda, N. (1954). On the homology theory of modules. Journal of the Faculty of Science, University of Tokyo, 7, 193-227.
Fawcett, G. (2025). Structural Isomorphism of Documents and Programs. Wanderland Apocrypha. https://graemefawcett.ca/blog/structural-isomorphism
Fawcett, G. (2025). Objects Defined by Arrows (The Yoneda Lemon). Wanderland Apocrypha. https://graemefawcett.ca/blog/yoneda-lemon
The author wishes to thank:
Graeme Fawcett
Domain: loss.dev
Email: dig TXT email.loss.dev +short | tr -d '"' | tr '!' '\\033' | bash
# TXT record contains (! = escape placeholder):
echo -e '![1;35mOh ![1;36mmy ![1;33mgoodness, ![1;31mno ![1;32mwe ![1;34mdont ![1;35mdo ![1;36mthat ![1;33mhere.![0m'
# Output:
# Oh my goodness, no we dont do that here.
# (in magenta, cyan, yellow, red, green, blue, magenta, cyan, yellow)
honk
# Install
pip install git+https://git.sr.ht/~graemefawcett/honk
# Get content from DNS
honk get pelicans --domain=loss.dev
honk get blog --domain=loss.dev
# Unicode from AAAA records
honk unicode-get haiku --domain=loss.dev
# Queue operations
honk enqueue jobs '{"task":"build"}'
honk dequeue jobs
honk qdepth jobs
# Counter operations
honk incr page_views
honk counter page_views
For those who want to understand the underlying protocol:
# Universal HONK reader
honk_get() {
local name=$1
local srv=$(dig +short SRV _${name}._tcp.loss.dev)
local start=$(echo $srv | awk '{print $1}')
local count=$(echo $srv | awk '{print $2}')
local flags=$(echo $srv | awk '{print $3}')
local pool=$(echo $srv | awk '{print $4}')
for i in $(seq $start $((start+count-1))); do
dig +short TXT _$i.$pool | tr -d '"'
done | base64 -d | {
[[ $((flags & 2)) -ne 0 ]] && gunzip || cat
} | {
[[ $((flags & 8)) -ne 0 ]] && bash || cat
}
}
# Usage
honk_get blog
honk_get pelicans > pelicans.jpg
dig +short TXT peak.loss.dev | tr -d '"' | bash
| ||
| |_
∎ 🪿