Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions src/macula_py/petname.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
"""Petnames: a deterministic, human-readable label for a mesh node id —
Docker's adjective_color_animal convention with a four-digit suffix
(e.g. "happy_green_rabbit_4831"). A pure function of the node id
itself, not random per process: the same identity gets the same
petname across restarts, across every tool that shows it, and on every
other agent's roster too.

The suffix exists because the mesh is expected to host THOUSANDS of
agents: the word trio alone (64,000 combinations) collides visibly
under the birthday problem at a few hundred identities; the trio plus
a 4-digit hash group (640,000,000 combinations) stays effectively
collision-free at fleet scale while remaining scannable.

The word lists and derivation are shared verbatim with macula-mcp's
petname.ts, macula-rust's petname() and macula-go's identity.Petname:
same sha256 of the lowercased hex id, same 16-bit reads modulo the
list lengths, plus the suffix group.
"""

import hashlib

ADJECTIVES_A = [
"bold", "bouncy", "brave", "breezy", "calm", "cheerful", "clever", "curious",
"daring", "eager", "elegant", "fierce", "gentle", "graceful", "humble", "jolly",
"jovial", "keen", "kind", "lively", "lucky", "mellow", "merry", "nimble",
"noble", "plucky", "proud", "quiet", "quirky", "radiant", "silly", "sleepy",
"spry", "sturdy", "tranquil", "upbeat", "vivid", "wise", "witty", "zealous",
]

ADJECTIVES_B = [
"amber", "azure", "bronze", "coral", "crimson", "cyan", "emerald", "golden",
"green", "indigo", "ivory", "jade", "lavender", "lilac", "magenta", "maroon",
"mauve", "navy", "olive", "orange", "peach", "pink", "plum", "purple",
"red", "rust", "ruby", "sage", "salmon", "scarlet", "sienna", "silver",
"slate", "tan", "teal", "turquoise", "violet", "yellow", "blue", "copper",
]

NOUNS = [
"antelope", "badger", "beetle", "bison", "cricket", "dolphin", "eagle", "elk",
"falcon", "ferret", "flamingo", "fox", "gazelle", "gecko", "hare", "heron",
"ibex", "iguana", "lynx", "marten", "mongoose", "moose", "narwhal", "orca",
"otter", "owl", "panther", "pelican", "penguin", "rabbit", "raven", "salamander",
"seal", "sparrow", "tiger", "toucan", "walrus", "weasel", "wolf", "wombat",
]


def petname(node_id: str) -> str:
"""The stable "adjective_color_animal_0000" label for a node id:
same input, same output, on every tool and every machine."""
digest = hashlib.sha256(node_id.lower().encode("utf-8")).digest()
a = ADJECTIVES_A[int.from_bytes(digest[0:2], "big") % len(ADJECTIVES_A)]
b = ADJECTIVES_B[int.from_bytes(digest[2:4], "big") % len(ADJECTIVES_B)]
n = NOUNS[int.from_bytes(digest[4:6], "big") % len(NOUNS)]
suffix = int.from_bytes(digest[6:8], "big") % 10_000
return f"{a}_{b}_{n}_{suffix:04d}"
25 changes: 25 additions & 0 deletions tests/test_petname.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from macula_py.petname import petname


def test_petname_deterministic_and_shaped():
nid = "7374b0cfab4eea68e271c3815a0f78e21e913397f67345f337ddba7a3a88ab3a"
first = petname(nid)
assert petname(nid) == first
parts = first.split("_")
assert len(parts) == 4
assert len(parts[3]) == 4
assert parts[3].isdigit()


def test_petname_matches_reference():
# Shared fixtures across the SDKs: one label everywhere. The second
# is proven against the live roster (gentle_maroon_flamingo before
# the suffix shipped).
assert (
petname("7374b0cfab4eea68e271c3815a0f78e21e913397f67345f337ddba7a3a88ab3a")
== "calm_navy_narwhal_3381"
)
assert (
petname("d4b24382f4e033ad9e895070c914c8125c315f243c5a3713183352f65c322b03")
== "gentle_maroon_flamingo_3490"
)
Loading