gabriel / muse public
domain_cmd.py python
330 lines 11.7 KB
Raw
sha256:2a703f78341332ef0beb9856d2267de6aec89b3883c31519b6900b667d026e62 chore: delete muse/prose domain — hallucinated, never existed Sonnet 4.6 minor ⚠ breaking 7 days ago
1 """muse domain — hash-derived domain index tooling.
2
3 Every Muse domain integer is computed from its canonical name string via::
4
5 index = int.from_bytes(sha256(name.encode("utf-8"))[:4], "big") & 0x7FFFFFFF
6
7 This makes the domain namespace open and decentralised: any third-party
8 domain derives its index without a central registry. The registry
9 (``gabriel/muse-domains`` on MuseHub) is a human-readable annotation layer —
10 it does not gate key derivation.
11
12 Subcommands::
13
14 muse domain index <name> Compute the index for any domain name.
15 muse domain lookup <name> Look up a name in the local registry.
16 muse domain lookup --index <n> Reverse-lookup an integer to a name.
17 muse domain list List all locally registered domains.
18 muse domain check <name> Exit 0 if registered, 1 if not.
19
20 Registry location
21 -----------------
22
23 The registry is read from, in order:
24
25 1. ``$MUSE_DOMAIN_REGISTRY`` env var (path to a ``registry.json`` file).
26 2. ``~/.muse/domain-registry.json`` (cached copy from the hub).
27 3. The built-in seed registry shipped with this muse release.
28
29 For air-gapped use, copy ``registry.json`` from ``gabriel/muse-domains`` and
30 set ``MUSE_DOMAIN_REGISTRY`` to point at it.
31
32 JSON schemas
33 ------------
34
35 ``muse domain index --json``::
36
37 {
38 "name": "muse/music",
39 "index": 1755707987,
40 "registered": true,
41 "entry": { "description": "...", "owner": "gabriel", ... }
42 }
43
44 ``muse domain list --json``::
45
46 {
47 "domains": [
48 { "name": "muse/identity", "index": 1660078172, ... },
49 ...
50 ],
51 "total": 8
52 }
53
54 ``muse domain check --json``::
55
56 { "name": "acme/gaming", "registered": false }
57 """
58
59 import argparse
60 import json
61 import logging
62 import os
63 import pathlib
64 import sys
65 from typing import TypedDict
66
67 from muse.core.envelope import EnvelopeJson, make_envelope
68 from muse.core.errors import ExitCode
69 from muse.core.hdkeys import domain_index
70 from muse.core.paths import user_domain_registry_path
71 from muse.core.timing import start_timer
72 from muse.core.types import load_json_file
73
74 logger = logging.getLogger(__name__)
75
76
77 class _DomainEntry(TypedDict, total=False):
78 name: str
79 index: int
80 description: str
81 owner: str
82 registered_at: str | None
83
84
85 # ---------------------------------------------------------------------------
86 # Seed registry — shipped with the muse binary for offline use.
87 # Authoritative source: gabriel/muse-domains on MuseHub.
88 # ---------------------------------------------------------------------------
89 _SEED_REGISTRY: list[_DomainEntry] = [
90 {
91 "name": "muse/identity",
92 "index": 1660078172,
93 "description": "Cross-domain authentication. MSign HTTP signing, MuseHub registration, the passport key for any identity on the network.",
94 "owner": "gabriel",
95 "registered_at": None,
96 },
97 {
98 "name": "muse/payments",
99 "index": 284229149,
100 "description": "MPay claims and financial settlement.",
101 "owner": "gabriel",
102 "registered_at": None,
103 },
104 {
105 "name": "muse/code",
106 "index": 678195575,
107 "description": "Software version control. Commit provenance, code-review attestations, symbol-level semantic diffs.",
108 "owner": "gabriel",
109 "registered_at": None,
110 },
111 {
112 "name": "muse/music",
113 "index": 1755707987,
114 "description": "Stori audio production. Project signing, master ownership, stem provenance.",
115 "owner": "gabriel",
116 "registered_at": None,
117 },
118 {
119 "name": "muse/midi",
120 "index": 1444628350,
121 "description": "Maestro symbolic music. NL-to-MIDI content signing, score provenance.",
122 "owner": "gabriel",
123 "registered_at": None,
124 },
125 {
126 "name": "muse/prose",
127 "index": 1658731548,
128 "description": "Long-form document signing. Markdown, plaintext, structured prose.",
129 "owner": "gabriel",
130 "registered_at": None,
131 },
132 {
133 "name": "muse/blockchain",
134 "index": 1556829714,
135 "description": "On-chain operations. ERC-8004 identity, ERC-721 masters, ERC-1155 marketplace, AVAX settlement. secp256k1 keys via Bitcoin seed HMAC root.",
136 "owner": "gabriel",
137 "registered_at": None,
138 },
139 {
140 "name": "muse/generic",
141 "index": 2023564266,
142 "description": "Repos and entities with no registered domain plugin. First-class explicit value — never an empty string or None.",
143 "owner": "gabriel",
144 "registered_at": None,
145 },
146 ]
147
148 def _load_registry() -> list[_DomainEntry]:
149 """Return the domain registry, preferring local overrides over the seed."""
150 paths = [
151 os.environ.get("MUSE_DOMAIN_REGISTRY", ""),
152 str(user_domain_registry_path()),
153 ]
154 for p in paths:
155 if p:
156 data = load_json_file(pathlib.Path(p))
157 if isinstance(data, dict):
158 return list(data.get("domains", []))
159 return _SEED_REGISTRY
160
161 def _lookup_by_name(registry: list[_DomainEntry], name: str) -> _DomainEntry | None:
162 for entry in registry:
163 if entry.get("name") == name:
164 return entry
165 return None
166
167 def _lookup_by_index(registry: list[_DomainEntry], idx: int) -> _DomainEntry | None:
168 for entry in registry:
169 if entry.get("index") == idx:
170 return entry
171 return None
172
173 # ---------------------------------------------------------------------------
174 # Subcommand: index
175 # ---------------------------------------------------------------------------
176
177 def _run_index(args: argparse.Namespace) -> None:
178 elapsed = start_timer()
179 name: str = args.name
180 idx = domain_index(name)
181 registry = _load_registry()
182 entry = _lookup_by_name(registry, name)
183 registered = entry is not None
184
185 if args.json_out:
186 out = {
187 **make_envelope(elapsed),
188 "name": name,
189 "index": idx,
190 "registered": registered,
191 }
192 if entry is not None:
193 out["entry"] = entry
194 print(json.dumps(out))
195 else:
196 print(f"name: {name}")
197 print(f"index: {idx}")
198 print(f"registered: {registered}")
199 if entry:
200 print(f"owner: {entry.get('owner', '')}")
201 print(f"desc: {entry.get('description', '')}")
202
203 # ---------------------------------------------------------------------------
204 # Subcommand: lookup
205 # ---------------------------------------------------------------------------
206
207 def _run_lookup(args: argparse.Namespace) -> None:
208 elapsed = start_timer()
209 registry = _load_registry()
210
211 if args.index is not None:
212 entry = _lookup_by_index(registry, args.index)
213 if entry is None:
214 msg = {"error": f"No domain registered with index {args.index}"}
215 print(json.dumps(msg), file=sys.stderr)
216 raise SystemExit(ExitCode.USER_ERROR)
217 idx = args.index
218 name = entry["name"]
219 else:
220 if not args.name:
221 print(json.dumps({"error": "Provide <name> or --index"}), file=sys.stderr)
222 raise SystemExit(ExitCode.USER_ERROR)
223 name = args.name
224 idx = domain_index(name)
225 entry = _lookup_by_name(registry, name)
226 if entry is None:
227 msg = {"error": f"Domain '{name}' not found in registry"}
228 print(json.dumps(msg), file=sys.stderr)
229 raise SystemExit(ExitCode.USER_ERROR)
230
231 if args.json_out:
232 print(json.dumps({**make_envelope(elapsed), **entry, "index": idx}))
233 else:
234 print(f"name: {name}")
235 print(f"index: {idx}")
236 print(f"owner: {entry.get('owner', '')}")
237 print(f"desc: {entry.get('description', '')}")
238 if entry.get("registered_at"):
239 print(f"registered: {entry['registered_at']}")
240
241 # ---------------------------------------------------------------------------
242 # Subcommand: list
243 # ---------------------------------------------------------------------------
244
245 def _run_list(args: argparse.Namespace) -> None:
246 elapsed = start_timer()
247 registry = _load_registry()
248
249 if args.json_out:
250 print(json.dumps({
251 **make_envelope(elapsed),
252 "domains": registry,
253 "total": len(registry),
254 }))
255 else:
256 for entry in registry:
257 print(f"{entry['name']:30s} {entry['index']}")
258
259 # ---------------------------------------------------------------------------
260 # Subcommand: check
261 # ---------------------------------------------------------------------------
262
263 def _run_check(args: argparse.Namespace) -> None:
264 elapsed = start_timer()
265 name: str = args.name
266 registry = _load_registry()
267 entry = _lookup_by_name(registry, name)
268 registered = entry is not None
269
270 if args.json_out:
271 print(json.dumps({**make_envelope(elapsed), "name": name, "registered": registered}))
272 else:
273 status = "registered" if registered else "not registered"
274 print(f"{name}: {status}")
275
276 if not registered:
277 raise SystemExit(1)
278
279 # ---------------------------------------------------------------------------
280 # Registration
281 # ---------------------------------------------------------------------------
282
283 def register(subparsers: "argparse._SubParsersAction[argparse.ArgumentParser]") -> None:
284 """Register the ``muse domain`` namespace."""
285 parser = subparsers.add_parser(
286 "domain",
287 help="Hash-derived domain index tooling — compute, lookup, list, check.",
288 description=__doc__,
289 formatter_class=argparse.RawDescriptionHelpFormatter,
290 )
291 domain_subs = parser.add_subparsers(dest="domain_subcmd", metavar="SUBCMD")
292 domain_subs.required = True
293
294 # ── index ──────────────────────────────────────────────────────────
295 p_index = domain_subs.add_parser(
296 "index",
297 help="Compute the hash-derived index for a domain name.",
298 )
299 p_index.add_argument("name", help="Canonical domain name, e.g. 'muse/music'.")
300 p_index.add_argument("--json", "-j", action="store_true", dest="json_out")
301 p_index.set_defaults(func=_run_index)
302
303 # ── lookup ─────────────────────────────────────────────────────────
304 p_lookup = domain_subs.add_parser(
305 "lookup",
306 help="Look up a domain name (or --index integer) in the registry.",
307 )
308 p_lookup.add_argument("name", nargs="?", default=None,
309 help="Canonical domain name to look up.")
310 p_lookup.add_argument("--index", type=int, default=None,
311 help="Reverse-lookup: find the name for this integer index.")
312 p_lookup.add_argument("--json", "-j", action="store_true", dest="json_out")
313 p_lookup.set_defaults(func=_run_lookup)
314
315 # ── list ───────────────────────────────────────────────────────────
316 p_list = domain_subs.add_parser(
317 "list",
318 help="List all locally registered domains.",
319 )
320 p_list.add_argument("--json", "-j", action="store_true", dest="json_out")
321 p_list.set_defaults(func=_run_list)
322
323 # ── check ──────────────────────────────────────────────────────────
324 p_check = domain_subs.add_parser(
325 "check",
326 help="Exit 0 if the name is registered, 1 if not.",
327 )
328 p_check.add_argument("name", help="Canonical domain name to check.")
329 p_check.add_argument("--json", "-j", action="store_true", dest="json_out")
330 p_check.set_defaults(func=_run_check)
File History 1 commit
sha256:2a703f78341332ef0beb9856d2267de6aec89b3883c31519b6900b667d026e62 chore: delete muse/prose domain — hallucinated, never existed Sonnet 4.6 minor 7 days ago