1 · Read a record in 40 lines
Start here, because it proves the rest. A BEACON record is a plain-text inscription on Dogecoin. If you can fetch a transaction from any Dogecoin explorer with a transaction API, you can read every record ever written, with no library, no SDK and nothing from us.
#!/usr/bin/env python3
"""Read a BEACON record from a Dogecoin reveal txid. No dependencies."""
import json, sys, urllib.request
BOOK = "https://dogebook.example/api/v2" # any Blockbook instance
def tx(t): return json.load(urllib.request.urlopen(BOOK + "/tx/" + t))
def chunks(hexstr): # parse script -> push chunks
b, out, i = bytes.fromhex(hexstr or ""), [], 0
while i < len(b):
op = b[i]; i += 1
if op == 0x00: out.append((op, b""))
elif op < 0x4c: out.append((op, b[i:i+op])); i += op
elif op in (0x4c,0x4d,0x4e):
w = {0x4c:1, 0x4d:2, 0x4e:4}[op]
n = int.from_bytes(b[i:i+w], "little"); i += w
out.append((op, b[i:i+n])); i += n
else: out.append((op, None))
return out
def number(c): # numberToChunk(), inverted
op, data = c
if op == 0x00: return 0
if 0x51 <= op <= 0x60: return op - 0x50 # OP_1..OP_16
return int.from_bytes(data, "little")
def inscription(reveal): # walk the P2SH reveal chain
per_tx, txid = [], reveal
while True:
t = tx(txid)
cs = chunks(t["vin"][0].get("hex"))[:-2] # drop sig + redeemScript
if not cs: raise SystemExit("no ord envelope")
per_tx.append(cs)
if cs[0][1] == b"ord": break # reached the marker
txid = t["vin"][0]["txid"]
cs = [c for part in reversed(per_tx) for c in part]
nparts = number(cs[1])
ctype = cs[2][1].decode()
data, i = bytearray(), 3
for _ in range(nparts):
i += 1; data += cs[i][1] or b""; i += 1 # skip parts-remaining
return ctype, bytes(data)
ctype, payload = inscription(sys.argv[1].lower())
assert ctype.startswith("text/plain")
text = payload.decode("utf-8")
assert text.splitlines()[0].strip() == "BEACON/1"
print(text)
chain.py, ~300 lines) is this same algorithm with caching, error
handling and custody tracing bolted on. Nothing in it is privileged.2 · The record grammar
Formally, in ABNF (RFC 5234), over UTF-8 octets:
record = magic-line 1*( LF line )
magic-line = %s"BEACON/1" ; exact, case-sensitive, no BOM
line = blank / comment / field / junk
blank = *WSP
comment = *WSP "#" *VCHAR
field = *WSP key ":" value *WSP
key = 1*8( %x41-5A / %x30-39 ) ; [A-Z0-9]{1,8}
value = *( %x20-10FFFF ) ; may itself contain ":"
junk = *VCHAR ; no colon, or bad key -> IGNORED
Six rules make this survivable, and they are the whole reason the format is this boring:
| trailing CR | stripped — a record written on Windows parses identically |
| leading/trailing WSP | stripped on every line |
| first colon wins | ID:ISO11784:985141001234567 → key ID, value ISO11784:985141001234567 |
| whitespace around the key and the value | stripped, so N: Biscuit, N:Biscuit and N : Biscuit are the same. A value can never begin or end with whitespace |
| duplicate keys | first occurrence wins. Truncation at the end is therefore safe |
| unknown keys | preserved and ignored — never an error. This is what makes BEACON/1 extensible without a version bump |
Keys
| Key | Genesis | Meaning | Hard limit |
|---|---|---|---|
T | required | Profile token: PET or ASSET in draft-1 | [A-Z0-9]{2,16} |
ID | required | SCHEME:VALUE — what the beacon is about | value ≤ 128 |
N | required | Display name | ≤ 64 |
1–6 | optional | The six information lines; the profile fixes their meaning | ≤ 200 each |
S | optional | Status token; default ACTIVE | token |
A | optional | Delegated authority — a second Dogecoin address allowed to update | P2PKH address |
U | optional | A URL for more information | ≤ 200 |
P | — | Presence of P makes it an update, not a genesis. Value is the genesis txid, 64 lowercase hex | 64 hex |
X C M | reserved | cross-reference / certificate txid / key=value; extension list | — |
The machine-readable copy of the profiles, statuses, identifier schemes and
every limit above is served as JSON at /api/profiles
so a client never has to hard-code them.
Identifier normalisation — the one subtle rule
Records are stored exactly as written. Normalisation applies only when matching two identifiers:
def id_key(raw): # matches the reference implementation
scheme, _, value = raw.partition(":")
scheme = scheme.strip().upper()
value = value.strip()
for ch in " -.:": value = value.replace(ch, "") # separators are noise
return scheme + ":" + value.upper() # (colons included)
id_key("iso11784:985 141-001.234567") == id_key("ISO11784:985141001234567") # True
ISO11785 alias is
not folded. §5 of the specification lists ISO11785 as an alias of
ISO11784, but the reference id_key() above does not
collapse it — so a record written ID:ISO11785:<15 digits> will
not be found by a lookup for ISO11784:<same digits>.
Verified against the running parser on 2026-08-14. Until this is resolved,
write ISO11784, and if you are building an indexer, decide
deliberately whether to fold the alias and say which you did. Interoperating with
this resolver today means matching the code, not the prose.An unregistered scheme is not an error. A reader MUST accept it and treat the identifier as opaque. Scheme validation exists to warn a writer about a typo before they spend money; it is never a reason to refuse to read the chain. An indexer that drops records it does not recognise has broken rule 4.
3 · The Dogecoin envelope, byte by byte
BEACON deliberately invents nothing at the chain layer. It uses the ordinary ord-style doginal envelope, unchanged, so every existing Dogecoin inscription indexer already sees BEACON records without a line of new code. If you already have a doginals inscriber, you already have a BEACON writer.
3.1 The chunk sequence
The payload is encoded as a flat sequence of script chunks:
push("ord") ; the 3-byte ASCII marker
push(nParts) ; number chunk
push(contentType) ; "text/plain;charset=utf-8" as ASCII
for n in 0 .. nParts-1:
push(nParts - n - 1) ; number chunk: parts REMAINING after this one
push(parts[n]) ; ≤ 240 data bytes
3.2 Push encoding — get this wrong and the record is unreadable forever
| len ≤ 75 | single opcode byte = the length, then the data |
| 76 ≤ len ≤ 255 | 0x4c (OP_PUSHDATA1), 1 length byte, data |
| 256 ≤ len ≤ 65535 | 0x4d (OP_PUSHDATA2), 2 length bytes LE, data |
| n = 0 | 0x00 (OP_0) |
| 1 ≤ n ≤ 16 | 0x50 + n (OP_1 … OP_16) |
| n > 16 | little-endian magnitude, sign-padded: append an extra 0x00 whenever the top byte has its MSB set |
ord-dogecoin's
push_data_to_number() reads number chunks as unsigned
little-endian. A signed CScriptNum decoder reads the raw two bytes
[0x00, 0x80] as -0, i.e. zero. Emitting naïve 2-byte LE
for values in 32768–65535 would index correctly on ord today and
silently destroy the inscription under any signed reader — on a chain you cannot
edit. Pad to [0x00, 0x80, 0x00] and both decoders agree. For
records under 1 kB you will never reach these values, but an implementation that
gets it wrong will fail the first time somebody inscribes something large.3.3 The commit / reveal chain
Script data cannot simply be dropped into an output. It is committed inside a P2SH redeem script and revealed when that output is spent:
redeemScript (the "lock") = push(pubkey) OP_CHECKSIGVERIFY
OP_DROP × (number of envelope chunks in this hop)
OP_TRUE
scriptPubKey (the commit) = OP_HASH160 push(hash160(lock)) OP_EQUAL
scriptSig (the reveal) = <envelope chunks for this hop> push(signature) push(lock)
| MAX_CHUNK_LEN | 240 — data bytes per part |
| MAX_PAYLOAD_LEN | 1500 — serialized envelope-chunk bytes carried per transaction |
| MAX_SCRIPT_ELEMENT_SIZE | 520 — the redeem script is pushed as one stack item, so it must fit |
| INSCRIPTION_KOINU | 100000 (0.001 DOGE) — the value carried on the inscription output at every hop |
| signing | legacy SIGHASH_ALL over an arbitrary subscript — the lock script, not a P2PKH template |
For a record at BEACON's ≤ 1000-byte target the whole envelope fits in one data-bearing transaction, so the chain is exactly two transactions:
tx[0] "commit" funds a P2SH output whose redeem script hides the envelope
tx[1] "reveal-final" spends it, exposing the envelope in scriptSig,
and pays 0.001 DOGE to vout 0 = THE INSCRIPTION
ord-dogecoin's inscription updater derives
the id from the txid of the transaction whose input[0] scriptSig
first carried the ord marker — that is tx[1]. For a
two-transaction chain that is also the final reveal, so the two coincide and
nothing surfaces. For a payload that spans many data transactions they are
different, and recording the final txid gives you an id the indexer never
assigns: an inscription you cannot find, on a chain you cannot edit.
Keep a BEACON record in one data-bearing reveal — under ~1400 bytes — and
the beacon txid, the ord inscription id and the reveal txid are all the same
value. That is why §1 of the spec caps records at 1000 bytes.3.4 Reading it back
Start at the reveal txid. For each hop, take vin[0]'s scriptSig,
parse it into chunks, and discard the last two — those are the signature
and the redeem script. If the first remaining chunk is "ord" you
have reached the start; otherwise follow vin[0].txid back one hop.
Then reverse the per-transaction order (you walked backwards), concatenate, and
decode §3.1. Cap the walk — 64 hops is about 1 MB of payload — so a malformed
chain cannot spin forever.
The discovery rule for finding BEACON records among all inscriptions is
one line: content type begins text/plain and the first line is
exactly BEACON/1. That is all. There is no registry of
BEACON-issuing addresses, no allowlist, and nothing to join.
4 · Writing one yourself
Three routes, in increasing order of effort. All three produce byte-identical records; the chain cannot tell which you used, and neither can we.
Route A — any existing doginals inscriber
Because BEACON is an ordinary text/plain doginal, any tool that
can inscribe a text file already inscribes BEACON records. Write the record to a
file and inscribe it with the content type
text/plain;charset=utf-8:
$ cat > biscuit.txt <<'EOF'
BEACON/1
T:PET
ID:ISO11784:985141001234567
N:Biscuit
1:Beagle mix, 24 lb, tan and white
2:White blaze, notched left ear, red collar with a bell
3:H. Elsner
4:+1-650-649-4265
5:San Jose, CA
6:Diabetic, needs insulin twice daily. Reward for safe return.
S:ACTIVE
EOF
# inscribe with your doginals tool of choice, e.g.
$ node . mint <your-doge-address> text/plain;charset=utf-8 biscuit.txt
The reveal txid it prints is your beacon's permanent identity. Nothing else is required for the record to exist.
Route B — the open browser core we use ourselves
The signing core this site runs is a single dependency-free JavaScript file served from the wallet host, and you may use it directly. Keys never leave the page; there is no server call in the build step.
<script src="https://doge.doginaldog.shop/dogewallet-core.js"></script>
<script>
const text = "BEACON/1\nT:PET\nID:ISO11784:985141001234567\nN:Biscuit\n…";
const hex = [...new TextEncoder().encode(text)]
.map(b => b.toString(16).padStart(2, "0")).join("");
const built = DogeWallet.buildInscription({
wif: "<your WIF — stays in this page>",
contentType: "text/plain;charset=utf-8",
dataHex: hex,
recipientAddress: "<where the inscription should land>",
changeAddress: "<your change address>",
utxos: [ /* {txid, vout, value} — CARDINAL utxos only, see §5 */ ],
feeRateKoinuPerKb: "200000000" // 2 DOGE/kB
});
// built.txs = [{role, hex, txid, fee, size}, …] — broadcast IN ORDER.
</script>
Broadcast each hex in order to any Dogecoin node or public
broadcast endpoint. You do not have to use ours. If you want to,
POST /capi/chain/broadcast with {"hex": "…"} is open and
takes no key — it is a relay, and it is the only thing this box does for a
visitor's transaction. It never sees a private key, because the transaction
arrives already signed.
Route C — from scratch
§3 is complete. Implement the chunk encoder, the P2SH lock, legacy SIGHASH_ALL signing over the lock as subscript, and the funding walk. The two places implementations actually break are the sign-padded number encoding (§3.2) and the fee floor (§5) — not the cryptography.
5 · Fees, dust and no-RBF
These are Dogecoin facts, not BEACON policy, and they are where real money is lost.
| Rule | Why it bites |
|---|---|
| Dogecoin has no RBF. | A broadcast transaction cannot be fee-bumped. There is no second chance to raise the fee. Overpay slightly, always. |
| The fee floor is charged per started kilobyte. | A 1.87 kB reveal owes the 2 kB minimum. Paying 1.87 kB worth leaves it unmineable, permanently, with no way to fix it. |
| Every hop carries a sub-dust output. | The 0.001 DOGE inscription output is below the normal dust relay threshold, so rate × size alone will not get the transaction mined. An absolute per-transaction floor must be applied on top. |
| Never fund from an inscription-bearing UTXO. | Spending a UTXO that carries someone's doginal as ordinary change destroys it. Classify your UTXOs and fund only from cardinal ones. Fail closed: if you cannot classify a UTXO, do not spend it. |
| Persist before you broadcast. | Write the signed hex somewhere durable first. If you must re-broadcast, re-broadcast the identical bytes; a second, differently-signed attempt is a double-spend race you cannot call off. |
| Unsticking is CPFP, not replacement. | Spend the stuck transaction's output at a high fee. The only "cancel" is waiting for the mempool to drop it. |
This site inscribes at 200000000 koinu/kB (2 DOGE/kB), which at
BEACON record sizes costs a few US cents in total for the whole two-transaction
chain. You are free to pick your own rate; the protocol has no opinion.
6 · Authority and the fold
There is no account, no password and no registrar. The access-control model is one sentence: the holder of the genesis inscription output is the authority.
An update — a record carrying P:<genesis txid> — is
honoured if and only if it was inscribed by one of:
- an address that has held the genesis inscription (the custody chain, not merely the current holder — an update made by a past holder was authorised at the time it was made), or
- the address named in the genesis record's
Akey, if present.
"Inscribed by" means: an address that appears among the inputs funding the update transaction.
The fold algorithm
current = genesis.keys # start from the genesis record
authority = set(custody_chain) | ({genesis.A} if genesis.A else set())
for u in updates sorted by (block_height, block_time): # unconfirmed last
if not (set(u.inscriber_addresses) & authority): reject(u, "not authorised")
elif u.ID and id_key(u.ID) != id_key(genesis.ID): reject(u, "changes ID")
elif u.T and u.T != genesis.T: reject(u, "changes profile")
else:
for k, v in u.keys: if k != "P": current[k] = v # present keys only
Absent keys keep their previous value — an update is a patch, not a
replacement. ID and T may be echoed but never changed:
the identifier is what the beacon is.
Two consequences a writer must understand before spending:
- Transferring the inscription transfers the beacon. Sending the doginal hands the next holder the right to rewrite the record. For an asset that is the point: custody of the token is custody of the registration.
- Losing the key freezes the record. It stays readable forever and can
never be updated again. The remedy is a new genesis, cross-referenced from
the old one with
X.
7 · How indexing actually works
This is the part people assume is magic, so here is the plain mechanism, with its current limits stated rather than glossed.
Any valid BEACON record enters this index regardless of who wrote it, what tool they used, or whether they have ever heard of this site. The index has no concept of "our" records versus other people's. There is one code path, and these are the three ways a txid reaches it:
| Route | Trigger | Who can use it |
|---|---|---|
| Submission | POST /api/index {"txid": "…"} | Anyone. No key, no account. Rate-limited to 20/min per IP purely to keep the chain indexer from being hammered. |
| Resolution on first read | GET /api/record/<txid> or GET /r/<txid> for a txid never seen before | Anyone. Being "in our index" is not a precondition for resolving. The record is fetched from the chain, decoded, folded and stored on the spot. |
| Custody sweep | Automatic, on refresh of an existing beacon | Nobody has to do anything. Every address that has held the beacon is swept for further BEACON inscriptions naming it in P, so an owner who updates from the wallet holding the doginal is picked up without submitting anything. |
And the reason the gap is survivable: the index is a cache with no
authority. Every field in it is derived from Dogecoin and can be rebuilt from
Dogecoin. Delete /srv/data/beacon and nothing is lost but speed.
Any index that starts deciding things instead of caching them has broken
the guarantee.
8 · Resolver API reference
Base URL: https://pet.doginaldog.shop (pet vocabulary) or
https://beacon.hankelsner.tech (asset vocabulary). One service, two
front doors, identical data — the Host header picks the labels and
theme, never the content. All responses are JSON unless noted. There is no
authentication anywhere on this API.
$ curl -s https://pet.doginaldog.shop/api/health
{"ok": true, "service": "beacon", "spec": "BEACON/1"}
Index-wide counts. Cached 30 s.
{"beacons": 0, "by_status": {}, "by_profile": {}, "missing": 0}
The complete machine-readable vocabulary: profiles and their six line labels, status codes and per-profile display labels, every identifier scheme with its hint, and the field length limits. Cached 1 h. Fetch this instead of hard-coding.
The specification itself, as Markdown. text/markdown; charset=utf-8.
The folded record. ?refresh=1 forces a re-check against the chain
(rate-limited 20/min/IP; otherwise a 5-minute TTL applies). A txid that has never
been indexed is resolved from the chain on the spot.
{
"txid": "<64 hex>",
"inscription_id": "<64 hex>i0",
"view": { "name": …, "id": …, "status": …, "status_label": …,
"profile": "PET", "lines": [ {"label": …, "value": …}, … ] },
"keys": { "T": "PET", "ID": "…", "N": "…", "1": "…", … }, ← after folding
"genesis": { "keys": …, "unknown": …, "meta": { "text": …, "bytes": …,
"block_height": …, "block_time": …, "confirmations": …,
"inscribers": ["D…"] } },
"holder": { "address": "D…", "traced": true, "custody_chain": ["D…"],
"hops": 0, "note": null },
"authority": ["D…"],
"history": [ { "txid": …, "authorised": true, "changes": {"S": "MISSING"},
"block_height": …, "text": … } ],
"applied": 1,
"rejected": [ { "txid": …, "reason": "not authorised" } ],
"first_seen": …, "checked": …
}
holder.traced: false means the transfer trace stopped early (the
hop cap is 32) — the address shown may not be current, and a client MUST say so
rather than present it as fact.
Search the index. q matches, in priority order: exact normalised
identifier → txid prefix → substring of the name → substring of the identifier.
limit caps at 200.
$ curl -s 'https://pet.doginaldog.shop/api/lookup?q=985141001234567'
{"results": [ {"txid": …, "id": …, "name": …, "profile": "PET",
"status": "ACTIVE", "holder": "D…"} ], "count": 1}
Newest records first, by block time. limit caps at 100. Cached 30 s.
A QR code as SVG. text 1–900 chars, px 64–1024.
Cached 24 h.
Renders the certificate for a record. Query: theme,
title, lines (e.g. 1234), qr=0|1,
target (§10), value, host.
The human-facing record page — rendered entirely on the server with no
client JavaScript. That is deliberate: a scanned QR code has to resolve on a
stranger's locked-down phone, through a link-preview bot, on a bad connection, at
2 a.m. Anything that depends on a script executing is a lost pet. Carries
og: tags and robots: index,follow. Cached 60 s.
Ask the resolver to index a txid. This is the open door. No key. Costs nothing. Works for a record you inscribed with any tool, from any wallet, with no prior relationship to this site.
$ curl -s -X POST https://pet.doginaldog.shop/api/index \
-H 'Content-Type: application/json' \
-d '{"txid":"<your reveal txid>"}'
{"ok": true, "record": { … the folded record … }}
Submit an update txid and it is attached to its genesis
automatically — indexing that genesis first if this resolver has never seen it.
Errors: 400 malformed txid or not a valid BEACON genesis,
429 over 20/min, 502 the chain indexer is unreachable
(transient — retry; it is never proof the record does not exist).
Render a certificate SVG from a record body without inscribing anything. 60/min/IP.
Relay a signed transaction: {"hex": "…"}. Belongs to the shared
crypto engine, not to BEACON. Open, keyless, and optional — any Dogecoin node
will do the same job.
9 · Build a competing indexer
This is an invited, expected outcome, not a threat. If you index BEACON better than we do, the protocol wins and the pets get found. Everything you need:
- Discover. Scan inscriptions whose content type starts
text/plainand whose first line is exactlyBEACON/1(§3.4). Any ord-dogecoin index, doggy.market-style API, or your own node will do. - Classify. No
Pkey → genesis, keyed by its own reveal txid. HasP→ update, belonging to the genesis it names. - Validate genesis. Require
T,ID,N. Warn on scheme mismatches; never reject on them. - Trace custody. Follow the 0.001 DOGE output from the genesis txid. A doginal transfer keeps the inscription on output 0 of the spending transaction — the convention every Dogecoin wallet follows. Record every address it passes through; that list is the authority set (§6).
- Fold. Apply §6 in block order. Publish the rejects.
- Index for lookup on
id_key(ID)(§2), not the raw string. - Never decide anything. If your index and the chain disagree, the chain is right and your index is broken.
One practical note that cost us real time: Blockbook has no outpoint→spender
index. The cheap way to find the spender of an inscription output is to query the
address holding it with details=txs&from=<block height> —
one call returning full transactions, with everything before the funding block
discarded. Because results come newest-first, reaching the funding transaction
itself means everything remaining is older, so you stop there. The naïve version
took 38 seconds on a busy wallet.
10 · Certificates
A beacon MAY additionally be minted as a visual certificate: a second
inscription with content type image/svg+xml. It is decorative and
evidentiary. It is not required to read or resolve a beacon, and an
indexer must never depend on one.
A certificate MUST embed, inside the SVG's <desc> element, a
self-describing block so an indexer can link it back to the record without
trusting a filename:
<desc>BEACON/1 CERT
P:<genesis txid>
ID:<scheme>:<value>
N:<name></desc>
The genesis record MAY then be updated with C:<certificate txid>
to point forward at it. A certificate MAY carry a QR code encoding exactly one
of: txid, resolver
(https://<host>/r/<txid>), explorer,
contact (a tel:/mailto: from line 4),
url (the record's U), or custom.
A resolver URL points at a resolver. If it is gone, the txid inside the certificate still resolves against the chain — which is why encoding the bare txid is the most durable choice and encoding only a resolver URL is the least.
11 · Conformance vectors
If your parser agrees with all of these, it agrees with ours. Every one of them encodes a rule that has a real failure behind it.
INPUT EXPECTED
─────────────────────────────────────── ────────────────────────────────────────
"BEACON/1\nN:A\nN:B" N == "A" (first duplicate wins)
"BEACON/1\r\nT:PET\r\n" T == "PET" (CR stripped)
"BEACON/1\n N : x " N == "x" (the KEY is stripped too,
so "N " is key "N")
"BEACON/1\nN: Biscuit" N == "Biscuit" (leading space gone)
"BEACON/1\nN: Biscuit" N == "Biscuit" (the value is FULLY
stripped: a value can
never begin or end
with whitespace)
"BEACON/1\n#N:x\nN:y" N == "y" (comment ignored)
"BEACON/1\n\n\nN:y" N == "y" (blank lines ignored)
"BEACON/1\nID:ISO11784:985141001234567" ID value ==
"ISO11784:985141001234567"
"BEACON/1\nZZ:future" ZZ preserved in `unknown`, NOT an error
"BEACON/1\nnope" line ignored, record still valid
"beacon/1\nN:x" NOT a BEACON record (magic is exact)
" BEACON/1\nN:x" IS a BEACON record (leading WSP stripped)
"\n# hi\nBEACON/1\nN:x" IS a BEACON record (magic = first
non-blank, non-comment line)
"BEACON/1\nT:PET\nID:CUSTOM:x\nN:n" valid genesis
"BEACON/1\nP:<64 hex>\nS:MISSING" valid update, changes S only
id_key("iso11784:985 141-001.234567") == id_key("ISO11784:985141001234567")
id_key("ISO11785:985141001234567") != id_key("ISO11784:985141001234567")
^ the alias is NOT folded -- see the
discrepancy note in section 2
number chunk 0 -> 0x00 (OP_0)
number chunk 1 -> 0x51 (OP_1)
number chunk 16 -> 0x60 (OP_16)
number chunk 17 -> 0x01 0x11
number chunk 128 -> 0x02 0x80 0x00 (sign-padded)
number chunk 32768 -> 0x03 0x00 0x80 0x00 (sign-padded — see §3.2)
push of 3 bytes -> 0x03 <data>
push of 240 bytes -> 0x4c 0xf0 <data> (PUSHDATA1)
The txids in the worked examples throughout this site are illustrative
placeholders, not real records. At the time of writing the index holds the count
shown by /api/stats — check it rather than
trusting any number written on a page.
12 · Permanence rules
Permanent and public are the same sentence here. An update can supersede information; nothing can erase it.
- No home street address. Line 5 is an area — a town, a postcode, a site name. A finder needs to know roughly where; nobody needs your door.
- No government identity numbers, no payment details, no passwords.
- Use a contact channel you are willing to keep or abandon — a phone number, an email address, or a URL you control. It is permanent once written.
- Assume everything ever written stays readable. Superseding a phone number does not delete the old one; it only stops it being the current answer.
13 · For AI agents
There is a memory file written for you rather than for a person: a single self-contained document with the format, the rules, the endpoints, the failure modes and worked call sequences, in a form you can load into context and act from without reading this page.
/agent.md — the agent memory file served from the API too /llms.txt
If you are an agent acting for someone: you can compose and validate a record, resolve any beacon, and submit a txid for indexing entirely through the endpoints in §8, none of which need a key. You cannot sign for them — signing needs their private key, and that is deliberate. Nothing on this box will ever ask you for one, and any surface that does is not us.
14 · Licence and non-ownership
BEACON/1 is a specification, not a product. We do not own it, we cannot revoke it, and there is nothing to license. Implement it, fork it, compete with it, ship a better registry, charge for your version, or write a profile we never thought of — no attribution required and no permission asked.
The parts of this that are ours are the two ordinary things: the code we wrote, and the instance we operate. Both are replaceable. The chain is the registry; everything else, including this site, is a convenience that should be able to disappear without taking anything with it. If this page is ever unreachable, §1 through §6 are enough to rebuild all of it from the chain alone.