gabriel / muse public
domain_cmd.py python
323 lines 11.5 KB
Raw
sha256:2a703f78341332ef0beb9856d2267de6aec89b3883c31519b6900b667d026e62 chore: delete muse/prose domain — hallucinated, never existed Sonnet 4.6 minor ⚠ breaking 8 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/blockchain",
127 "index": 1556829714,
128 "description": "On-chain operations. ERC-8004 identity, ERC-721 masters, ERC-1155 marketplace, AVAX settlement. secp256k1 keys via Bitcoin seed HMAC root.",
129 "owner": "gabriel",
130 "registered_at": None,
131 },
132 {
133 "name": "muse/generic",
134 "index": 2023564266,
135 "description": "Repos and entities with no registered domain plugin. First-class explicit value — never an empty string or None.",
136 "owner": "gabriel",
137 "registered_at": None,
138 },
139 ]
140
141 def _load_registry() -> list[_DomainEntry]:
142 """Return the domain registry, preferring local overrides over the seed."""
143 paths = [
144 os.environ.get("MUSE_DOMAIN_REGISTRY", ""),
145 str(user_domain_registry_path()),
146 ]
147 for p in paths:
148 if p:
149 data = load_json_file(pathlib.Path(p))
150 if isinstance(data, dict):
151 return list(data.get("domains", []))
152 return _SEED_REGISTRY
153
154 def _lookup_by_name(registry: list[_DomainEntry], name: str) -> _DomainEntry | None:
155 for entry in registry:
156 if entry.get("name") == name:
157 return entry
158 return None
159
160 def _lookup_by_index(registry: list[_DomainEntry], idx: int) -> _DomainEntry | None:
161 for entry in registry:
162 if entry.get("index") == idx:
163 return entry
164 return None
165
166 # ---------------------------------------------------------------------------
167 # Subcommand: index
168 # ---------------------------------------------------------------------------
169
170 def _run_index(args: argparse.Namespace) -> None:
171 elapsed = start_timer()
172 name: str = args.name
173 idx = domain_index(name)
174 registry = _load_registry()
175 entry = _lookup_by_name(registry, name)
176 registered = entry is not None
177
178 if args.json_out:
179 out = {
180 **make_envelope(elapsed),
181 "name": name,
182 "index": idx,
183 "registered": registered,
184 }
185 if entry is not None:
186 out["entry"] = entry
187 print(json.dumps(out))
188 else:
189 print(f"name: {name}")
190 print(f"index: {idx}")
191 print(f"registered: {registered}")
192 if entry:
193 print(f"owner: {entry.get('owner', '')}")
194 print(f"desc: {entry.get('description', '')}")
195
196 # ---------------------------------------------------------------------------
197 # Subcommand: lookup
198 # ---------------------------------------------------------------------------
199
200 def _run_lookup(args: argparse.Namespace) -> None:
201 elapsed = start_timer()
202 registry = _load_registry()
203
204 if args.index is not None:
205 entry = _lookup_by_index(registry, args.index)
206 if entry is None:
207 msg = {"error": f"No domain registered with index {args.index}"}
208 print(json.dumps(msg), file=sys.stderr)
209 raise SystemExit(ExitCode.USER_ERROR)
210 idx = args.index
211 name = entry["name"]
212 else:
213 if not args.name:
214 print(json.dumps({"error": "Provide <name> or --index"}), file=sys.stderr)
215 raise SystemExit(ExitCode.USER_ERROR)
216 name = args.name
217 idx = domain_index(name)
218 entry = _lookup_by_name(registry, name)
219 if entry is None:
220 msg = {"error": f"Domain '{name}' not found in registry"}
221 print(json.dumps(msg), file=sys.stderr)
222 raise SystemExit(ExitCode.USER_ERROR)
223
224 if args.json_out:
225 print(json.dumps({**make_envelope(elapsed), **entry, "index": idx}))
226 else:
227 print(f"name: {name}")
228 print(f"index: {idx}")
229 print(f"owner: {entry.get('owner', '')}")
230 print(f"desc: {entry.get('description', '')}")
231 if entry.get("registered_at"):
232 print(f"registered: {entry['registered_at']}")
233
234 # ---------------------------------------------------------------------------
235 # Subcommand: list
236 # ---------------------------------------------------------------------------
237
238 def _run_list(args: argparse.Namespace) -> None:
239 elapsed = start_timer()
240 registry = _load_registry()
241
242 if args.json_out:
243 print(json.dumps({
244 **make_envelope(elapsed),
245 "domains": registry,
246 "total": len(registry),
247 }))
248 else:
249 for entry in registry:
250 print(f"{entry['name']:30s} {entry['index']}")
251
252 # ---------------------------------------------------------------------------
253 # Subcommand: check
254 # ---------------------------------------------------------------------------
255
256 def _run_check(args: argparse.Namespace) -> None:
257 elapsed = start_timer()
258 name: str = args.name
259 registry = _load_registry()
260 entry = _lookup_by_name(registry, name)
261 registered = entry is not None
262
263 if args.json_out:
264 print(json.dumps({**make_envelope(elapsed), "name": name, "registered": registered}))
265 else:
266 status = "registered" if registered else "not registered"
267 print(f"{name}: {status}")
268
269 if not registered:
270 raise SystemExit(1)
271
272 # ---------------------------------------------------------------------------
273 # Registration
274 # ---------------------------------------------------------------------------
275
276 def register(subparsers: "argparse._SubParsersAction[argparse.ArgumentParser]") -> None:
277 """Register the ``muse domain`` namespace."""
278 parser = subparsers.add_parser(
279 "domain",
280 help="Hash-derived domain index tooling — compute, lookup, list, check.",
281 description=__doc__,
282 formatter_class=argparse.RawDescriptionHelpFormatter,
283 )
284 domain_subs = parser.add_subparsers(dest="domain_subcmd", metavar="SUBCMD")
285 domain_subs.required = True
286
287 # ── index ──────────────────────────────────────────────────────────
288 p_index = domain_subs.add_parser(
289 "index",
290 help="Compute the hash-derived index for a domain name.",
291 )
292 p_index.add_argument("name", help="Canonical domain name, e.g. 'muse/music'.")
293 p_index.add_argument("--json", "-j", action="store_true", dest="json_out")
294 p_index.set_defaults(func=_run_index)
295
296 # ── lookup ─────────────────────────────────────────────────────────
297 p_lookup = domain_subs.add_parser(
298 "lookup",
299 help="Look up a domain name (or --index integer) in the registry.",
300 )
301 p_lookup.add_argument("name", nargs="?", default=None,
302 help="Canonical domain name to look up.")
303 p_lookup.add_argument("--index", type=int, default=None,
304 help="Reverse-lookup: find the name for this integer index.")
305 p_lookup.add_argument("--json", "-j", action="store_true", dest="json_out")
306 p_lookup.set_defaults(func=_run_lookup)
307
308 # ── list ───────────────────────────────────────────────────────────
309 p_list = domain_subs.add_parser(
310 "list",
311 help="List all locally registered domains.",
312 )
313 p_list.add_argument("--json", "-j", action="store_true", dest="json_out")
314 p_list.set_defaults(func=_run_list)
315
316 # ── check ──────────────────────────────────────────────────────────
317 p_check = domain_subs.add_parser(
318 "check",
319 help="Exit 0 if the name is registered, 1 if not.",
320 )
321 p_check.add_argument("name", help="Canonical domain name to check.")
322 p_check.add_argument("--json", "-j", action="store_true", dest="json_out")
323 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 8 days ago