"""Instance identity — a stable, human-friendly name for this Navi install.
Every deployed Navi carries ``instance.json`` in its working directory
(gitignored): ``{"name": "yuki-grace", "instance_id": "<uuid>", "created_at": "..."}``.
The name is generated once (two female names from different cultures —
Japanese, American, Spanish; readable, speakable, collision-free at swarm
scale: 50×50×3 picks) and is meant to be renamed by hand if the owner wants.
The instance_id is the technical identity for audit and peer pinning.
Rename = edit the file, restart the server. Nothing else reads the name as
long-lived state, so this is safe.
"""
from __future__ import annotations
import json
import uuid as _uuid
from dataclasses import dataclass
from datetime import datetime, timezone
from pathlib import Path
_JAPANESE = [
"yuki", "sakura", "hana", "aki", "miku", "rin", "mei", "yui", "sana",
"nao", "kaori", "mika", "rika", "emi", "haruka", "asuka", "rei", "airi",
"kana", "honoka", "mitsuki", "natsumi", "sayuri", "suzume", "tamaki",
"tomoe", "ayame", "chiyo", "fumiko", "himari", "kaede", "kanna", "mai",
"mariko", "midori", "momoka", "natsuki", "noriko", "riko", "saori",
"shiori", "suzuka", "tsukiko", "umeko", "yoshiko", "yuzuki", "kirie",
"jun", "motoko", "satchiko",
]
_AMERICAN = [
"emma", "olivia", "ava", "mia", "chloe", "grace", "lily", "hannah",
"nora", "ruby", "alice", "clara", "daisy", "hazel", "iris", "jade",
"june", "leah", "lucy", "pearl", "rose", "sage", "stella", "violet",
"willa", "zoe", "amber", "brooke", "faith", "harmony", "josephine",
"katherine", "lydia", "madeline", "natalie", "paige", "riley", "scarlett",
"tessa", "trinity", "vivian", "abigail", "bethany", "delilah", "everly",
"jenna", "leona", "melody", "serenity", "tiffany",
]
_SPANISH = [
"sofia", "lucia", "valentina", "camila", "daniela", "carmen", "elena",
"gabriela", "ines", "lola", "marisol", "paulina", "raquel", "rosalia",
"serena", "teresa", "ximena", "alba", "bonita", "carlota", "esperanza",
"juanita", "mirta", "noelia", "paloma", "rosita", "adela", "belen",
"candela", "dolores", "estrella", "flor", "guadalupe", "jimena", "laura",
"miriam", "pilar", "rocio", "veronica", "yolanda", "catalina", "dalia",
"emilia", "fernanda", "gisela", "nayeli", "renata", "salma", "ursula",
"vanesa",
]
_NAME_POOLS = [_JAPANESE, _AMERICAN, _SPANISH]
IDENTITY_FILE = "instance.json"
@dataclass(frozen=True)
class InstanceIdentity:
name: str
instance_id: str
created_at: str
def generate_name() -> str:
"""Two female names from different cultures, e.g. ``yuki-grace``.
Single names would collide across a swarm (the peer channel addresses
machines by name); a mixed pair gives 7500×2 ordered combinations while
still reading as a pretty hyphenated female name.
"""
import secrets
pools = secrets.SystemRandom().sample(_NAME_POOLS, 2)
return f"{secrets.choice(pools[0])}-{secrets.choice(pools[1])}"
def load_identity(path: str | Path = IDENTITY_FILE) -> InstanceIdentity:
"""Read instance.json. Raises if the file is missing or malformed."""
data = json.loads(Path(path).read_text(encoding="utf-8"))
return InstanceIdentity(
name=str(data["name"]),
instance_id=str(data["instance_id"]),
created_at=str(data.get("created_at", "")),
)
def ensure_identity(path: str | Path = IDENTITY_FILE) -> InstanceIdentity:
"""Load the identity, creating the file on first run (install / boot)."""
p = Path(path)
if p.exists():
return load_identity(p)
identity = InstanceIdentity(
name=generate_name(),
instance_id=str(_uuid.uuid4()),
created_at=datetime.now(timezone.utc).isoformat(),
)
p.write_text(
json.dumps(
{
"name": identity.name,
"instance_id": identity.instance_id,
"created_at": identity.created_at,
},
indent=2,
)
+ "\n",
encoding="utf-8",
)
try:
p.chmod(0o644)
except OSError:
pass
return identity