灵符lingfu
L

ling-miao

↓ 0 ダウンロード

miao: a Ling-native version control system — temples, souls, rites and talismans instead of repos, branches, commits and tags; adaptive whole/delta/chunk storage; SSH temple fairs for sharing; a LingStationX-themed GUI; ROYGBIV everywhere

Windows · x86-64macOS · Intel x86-64Linux · x86-64LingOS · Pi 5 ARM64
キーワード vcs, version-control, git, miao, ling
lingfu install ling-miao
ホームページhttps://ling-lang.org
ライセンスApache-2.0 OR MIT
作者ling-lin
READMEコード / ファイルバージョン依存グラフ依存パッケージセキュリティビルド設定

コード / ファイル (268)

ling/remote.ling
# ━━━ remote.ling — fenxiang 分香: the incense network between temples ━━━
# `miao fair` opens your temple over REAL SSH (russh, in the Ling runtime):
#   · its own generated host key under .miao/fair/ — never your personal keys
#   · one-time credentials embedded in the share link
#   · the server answers ONLY the MIAO protocol: no shell, no exec, no fs
#   · read-only unless started with --tribute (separate write secret)
# Collaborators clone with `miao enshrine <link>` (spirit division),
# renew with `miao pilgrimage`, and send work up with `miao tribute`.
# fns prefixed nx_.

# link: miao+ssh://pilgrim:SECRET@HOST:PORT/NAME[?tribute=WSECRET]
# returns [host, port, user, pw, name, wpw]
fn nx_link_parse(link) {
    bind s = link
    if str_starts_with(s, "miao+ssh://") { bind s = str_slice(s, 11.0, 99999.0) }
    if str_starts_with(s, "ssh://") { bind s = str_slice(s, 6.0, 99999.0) }
    bind at = str_find(s, "@")
    if at < 0.0 { return ["", 0.0, "", "", "", ""] }
    bind creds = str_slice(s, 0.0, at)
    bind rest = str_slice(s, at + 1.0, 99999.0)
    bind c = str_find(creds, ":")
    if c < 0.0 { return ["", 0.0, "", "", "", ""] }
    bind user = str_slice(creds, 0.0, c)
    bind pw = str_slice(creds, c + 1.0, 99999.0)
    bind wpw = ""
    bind q = str_find(rest, "?tribute=")
    if q >= 0.0 {
        bind wpw = str_slice(rest, q + 9.0, 99999.0)
        bind rest = str_slice(rest, 0.0, q)
    }
    bind sl = str_find(rest, "/")
    bind name = ""
    if sl >= 0.0 {
        bind name = str_slice(rest, sl + 1.0, 99999.0)
        bind rest = str_slice(rest, 0.0, sl)
    }
    bind p = str_find(rest, ":")
    bind port = 7447.0
    bind host = rest
    if p >= 0.0 {
        bind host = str_slice(rest, 0.0, p)
        bind port = to_number(str_slice(rest, p + 1.0, 99.0))
    }
    return [host, port, user, pw, name, wpw]
}

# ── bundles ───────────────────────────────────────────────────────────
# everything reachable from our tips, minus everything reachable from
# the exclude tips (which the receiver already holds).
fn nx_bundle(exclude_csv) {
    bind ex_rites = list_new()
    if exclude_csv != "" {
        bind exs = split(exclude_csv, ",")
        bind i = 0.0
        while i < len(exs) {
            bind t = str_trim(list_get(exs, i))
            if t != "" {
                if file_exists(sx_dir() + "/relics/rite/" + t) {
                    bind anc = rt_ancestors(t)
                    bind k = 0.0
                    while k < len(anc) {
                        bind a = list_get(anc, k)
                        if ut_contains(ex_rites, a) == false { bind ex_rites = list_push(ex_rites, a) }
                        bind k = k + 1.0
                    }
                }
            }
            bind i = i + 1.0
        }
    }
    # spirits+pays the receiver already holds (reachable from excluded rites)
    bind ex_spirits = list_new()
    bind i = 0.0
    while i < len(ex_rites) {
        bind m = sx_rite_manifest(list_get(ex_rites, i))
        bind ids = list_get(m, 0.0)
        bind k = 0.0
        while k < len(ids) {
            bind id = list_get(ids, k)
            if ut_contains(ex_spirits, id) == false { bind ex_spirits = list_push(ex_spirits, id) }
            bind k = k + 1.0
        }
        bind i = i + 1.0
    }
    # rites to send
    bind souls = sx_souls()
    bind rites = list_new()
    bind i = 0.0
    while i < len(souls) {
        bind tip = sx_tip(list_get(souls, i))
        if tip != "" {
            bind anc = rt_ancestors(tip)
            bind k = 0.0
            while k < len(anc) {
                bind a = list_get(anc, k)
                if ut_contains(ex_rites, a) == false {
                    if ut_contains(rites, a) == false { bind rites = list_push(rites, a) }
                }
                bind k = k + 1.0
            }
        }
        bind i = i + 1.0
    }
    # objects: scroll pays, spirits, their pays, delta bases
    bind pays = list_new()
    bind spirits = list_new()
    bind i = 0.0
    while i < len(rites) {
        bind r = sx_rite_read(list_get(rites, i))
        bind sc = list_get(r, 0.0)
        if ut_contains(pays, sc) == false { bind pays = list_push(pays, sc) }
        bind m = sx_scroll_read(sc)
        bind ids = list_get(m, 0.0)
        bind k = 0.0
        while k < len(ids) {
            bind id = list_get(ids, k)
            if ut_contains(ex_spirits, id) == false {
                if ut_contains(spirits, id) == false { bind spirits = list_push(spirits, id) }
            }
            bind k = k + 1.0
        }
        bind i = i + 1.0
    }
    # expand spirits: delta bases + payload ids (chunk lists + chunks)
    bind spn = 0.0
    while spn < len(spirits) {
        bind id = list_get(spirits, spn)
        bind info = sx_spirit_info(id)
        bind strat = list_get(info, 0.0)
        if strat == "whole" {
            bind p = list_get(info, 1.0)
            if ut_contains(pays, p) == false { bind pays = list_push(pays, p) }
        }
        if strat == "delta" {
            bind base = list_get(info, 1.0)
            bind p = list_get(info, 2.0)
            if ut_contains(pays, p) == false { bind pays = list_push(pays, p) }
            if ut_contains(ex_spirits, base) == false {
                if ut_contains(spirits, base) == false { bind spirits = list_push(spirits, base) }
            }
        }
        if strat == "chunks" {
            bind lp = list_get(info, 1.0)
            if ut_contains(pays, lp) == false { bind pays = list_push(pays, lp) }
            bind lst = split(str_trim(sx_pay_text(lp)), "\n")
            bind k = 0.0
            while k < len(lst) {
                bind cid = list_get(lst, k)
                if cid != "" {
                    if ut_contains(pays, cid) == false { bind pays = list_push(pays, cid) }
                }
                bind k = k + 1.0
            }
        }
        bind spn = spn + 1.0
    }
    # assemble
    bind name = path_name(sx_root())
    bind t = "MIAOBUNDLE 1\ntemple " + name + "\n"
    bind i = 0.0
    while i < len(souls) {
        bind tip = sx_tip(list_get(souls, i))
        if tip != "" { bind t = t + "soul " + list_get(souls, i) + " " + tip + "\n" }
        bind i = i + 1.0
    }
    bind tals = sx_talismans()
    bind i = 0.0
    while i < len(tals) {
        bind t = t + "tal " + list_get(tals, i) + " " + sx_talisman_get(list_get(tals, i)) + "\n"
        bind i = i + 1.0
    }
    bind i = 0.0
    while i < len(rites) {
        bind id = list_get(rites, i)
        bind hr = file_read_hex(sx_dir() + "/relics/rite/" + id)
        if ut_is_bad(hr) == false { bind t = t + "obj rite " + id + "\nhex " + hr.unwrap() + "\n" }
        bind i = i + 1.0
    }
    bind i = 0.0
    while i < len(spirits) {
        bind id = list_get(spirits, i)
        bind hr = file_read_hex(sx_dir() + "/relics/spirit/" + id)
        if ut_is_bad(hr) == false { bind t = t + "obj spirit " + id + "\nhex " + hr.unwrap() + "\n" }
        bind i = i + 1.0
    }
    bind i = 0.0
    while i < len(pays) {
        bind id = list_get(pays, i)
        bind hr = file_read_hex(sx_dir() + "/relics/pay/" + id)
        if ut_is_bad(hr) == false { bind t = t + "obj pay " + id + "\nhex " + hr.unwrap() + "\n" }
        bind i = i + 1.0
    }
    bind t = t + "END\n"
    return t
}

# apply a bundle. mode: "clone" sets local souls; "pull" records mother tips.
# integrity: every object is hash-verified before it is enshrined.
# returns [rites_added, souls_csv ("name tipid" pairs joined by ",")]
fn nx_apply(text, mode, mother) {
    bind lines = split(text, "\n")
    bind i = 0.0
    bind added = 0.0
    bind souls_seen = ""
    bind cur_type = ""
    bind cur_id = ""
    while i < len(lines) {
        bind ln = list_get(lines, i)
        if str_starts_with(ln, "soul ") {
            bind rest = str_slice(ln, 5.0, 99999.0)
            bind sp = str_find(rest, " ")
            bind sname = str_slice(rest, 0.0, sp)
            bind stip = str_slice(rest, sp + 1.0, 99999.0)
            if souls_seen != "" { bind souls_seen = souls_seen + "," }
            bind souls_seen = souls_seen + sname + " " + stip
            if mode == "clone" { sx_set_tip(sname, stip) }
            if mode == "pull" { nx_mtip_set(mother, sname, stip) }
        } else if str_starts_with(ln, "tal ") {
            bind rest = str_slice(ln, 4.0, 99999.0)
            bind sp = str_find(rest, " ")
            if mode == "clone" {
                sx_talisman_set(str_slice(rest, 0.0, sp), str_slice(rest, sp + 1.0, 99999.0))
            }
        } else if str_starts_with(ln, "obj ") {
            bind parts = split(ln, " ")
            bind cur_type = list_get(parts, 1.0)
            bind cur_id = list_get(parts, 2.0)
        } else if str_starts_with(ln, "hex ") {
            bind hx = str_slice(ln, 4.0, 99999999.0)
            bind sub = "rite"
            if cur_type == "spirit" { bind sub = "spirit" }
            if cur_type == "pay" { bind sub = "pay" }
            bind dest = sx_dir() + "/relics/" + sub + "/" + cur_id
            if file_exists(dest) == false {
                bind tmp = sx_dir() + "/tmp/net"
                file_write_hex(tmp, hx)
                if cur_type == "spirit" {
                    # a spirit manifest is NAMED by the hash of the content it
                    # describes, not of its own bytes — validate its shape; the
                    # payloads it points at are themselves hash-verified.
                    bind mt = ut_read_text(tmp)
                    bind okm = false
                    if str_starts_with(mt, "whole|") { bind okm = true }
                    if str_starts_with(mt, "delta|") { bind okm = true }
                    if str_starts_with(mt, "chunks|") { bind okm = true }
                    if okm {
                        rename_file(tmp, dest)
                    } else {
                        remove_file(tmp)
                        print(cx_err("  ⚠ rejected a malformed spirit manifest"))
                    }
                } else if file_hash(tmp) == cur_id {
                    rename_file(tmp, dest)
                    if cur_type == "rite" { bind added = added + 1.0 }
                } else {
                    remove_file(tmp)
                    print(cx_err("  ⚠ rejected a corrupt " + cur_type + " object (hash mismatch)"))
                }
            }
        }
        bind i = i + 1.0
    }
    return [added, souls_seen]
}

# fast-forward local souls to the given "name tip,name tip" pairs when
# history allows (local tip missing, or an ancestor of the new tip).
fn nx_advance_souls(pairs_csv) {
    bind pairs = split(pairs_csv, ",")
    bind moved = 0.0
    bind i = 0.0
    while i < len(pairs) {
        bind pr = str_trim(list_get(pairs, i))
        if pr != "" {
            bind sp2 = str_find(pr, " ")
            bind sname = str_slice(pr, 0.0, sp2)
            bind stip = str_slice(pr, sp2 + 1.0, 99999.0)
            bind ours = sx_tip(sname)
            bind okmove = false
            if ours == "" { bind okmove = true }
            if ours == stip { bind okmove = false }
            if okmove == false {
                if ours != "" {
                    if ours != stip {
                        if ut_contains(rt_ancestors(stip), ours) { bind okmove = true }
                    }
                }
            }
            if okmove {
                sx_set_tip(sname, stip)
                bind moved = moved + 1.0
            }
        }
        bind i = i + 1.0
    }
    return moved
}

fn nx_mtip_set(mother, soul, tip) {
    bind lines = ut_read_lines(sx_dir() + "/mtips")
    bind out = ""
    bind i = 0.0
    while i < len(lines) {
        bind ln = list_get(lines, i)
        if str_starts_with(ln, mother + "|" + soul + "|") == false { bind out = out + ln + "\n" }
        bind i = i + 1.0
    }
    write_file(sx_dir() + "/mtips", out + mother + "|" + soul + "|" + tip + "\n")
    return 0.0
}

# ── mothers (remotes) ─────────────────────────────────────────────────
fn nx_mother_get(name) {
    bind lines = ut_read_lines(sx_dir() + "/mothers")
    bind i = 0.0
    while i < len(lines) {
        bind ln = list_get(lines, i)
        if str_starts_with(ln, name + "|") { return str_slice(ln, len(name) + 1.0, 99999.0) }
        bind i = i + 1.0
    }
    return ""
}

fn nx_mother_set(name, link) {
    bind lines = ut_read_lines(sx_dir() + "/mothers")
    bind out = ""
    bind i = 0.0
    while i < len(lines) {
        bind ln = list_get(lines, i)
        if str_starts_with(ln, name + "|") == false { bind out = out + ln + "\n" }
        bind i = i + 1.0
    }
    write_file(sx_dir() + "/mothers", out + name + "|" + link + "\n")
    return 0.0
}

fn nx_mother(args) {
    if len(args) < 1.0 {
        bind lines = ut_read_lines(sx_dir() + "/mothers")
        if len(lines) < 1.0 {
            print(cx_dim("this temple stands alone — no ancestral temples"))
            print(cx_dim("  affiliate with one: miao mother add origin <link>"))
            return 0.0
        }
        bind i = 0.0
        while i < len(lines) {
            bind ln = list_get(lines, i)
            bind sp = str_find(ln, "|")
            bind link = str_slice(ln, sp + 1.0, 99999.0)
            # never print the secret
            bind at = str_find(link, "@")
            bind safe = link
            if at >= 0.0 { bind safe = "miao+ssh://…@" + str_slice(link, at + 1.0, 99999.0) }
            print(cx_info("祖 " + ut_pad(str_slice(ln, 0.0, sp), 12.0)) + " " + cx_dim(safe))
            bind i = i + 1.0
        }
        return 0.0
    }
    bind op = list_get(args, 0.0)
    if op == "add" {
        if len(args) < 3.0 {
            print(cx_err("usage: miao mother add <name> <link>"))
            return 0.0
        }
        nx_mother_set(list_get(args, 1.0), list_get(args, 2.0))
        print(cx_good("✦ affiliated with ancestral temple: ") + cx_info(list_get(args, 1.0)))
        return 0.0
    }
    if op == "rm" || op == "remove" {
        bind name = list_get(args, 1.0)
        bind lines = ut_read_lines(sx_dir() + "/mothers")
        bind out = ""
        bind i = 0.0
        while i < len(lines) {
            bind ln = list_get(lines, i)
            if str_starts_with(ln, name + "|") == false { bind out = out + ln + "\n" }
            bind i = i + 1.0
        }
        write_file(sx_dir() + "/mothers", out)
        print(cx_warn("affiliation dissolved: " + name))
        return 0.0
    }
    print(cx_err("usage: miao mother [add <name> <link> | rm <name>]"))
    return 0.0
}

# ── the keeper's seal (印): per-person push identity ──────────────────
# Each keeper mints one Ed25519 seal, stored per-user (not per-repo).
# A temple owner then ENTRUSTS chosen keepers by their public seal
# (.miao/entrusted). An entrusted keeper may push to any fair of that
# temple — even a read-only one — by signing the tribute: the server
# verifies the signature, so no shared push secret ever travels.
# Private keys never leave the Ling runtime; only the seed is stored.

fn nx_seal_path() {
    bind p = env_get("MIAO_SEAL", "")
    if p != "" { return p }
    bind home = env_get("USERPROFILE", env_get("HOME", "."))
    return str_replace(home, "\\", "/") + "/.miao-seal"
}

# -> ed25519 handle, or 0-1 when no seal exists
fn nx_seal_handle() {
    bind p = nx_seal_path()
    if file_exists(p) == false { return 0.0 - 1.0 }
    bind seed = str_trim(ut_read_text(p))
    if len(seed) < 64.0 { return 0.0 - 1.0 }
    return ed25519_keygen_from_seed(seed)
}

fn nx_seal_cmd(args) {
    bind renew = false
    bind i = 0.0
    while i < len(args) {
        if list_get(args, i) == "--renew" { bind renew = true }
        bind i = i + 1.0
    }
    bind h = nx_seal_handle()
    if h < 0.0 || renew {
        write_file(nx_seal_path(), random_hex(32.0) + "\n")
        bind h = nx_seal_handle()
        print(cx_good("✦ a keeper's seal has been cut") + cx_dim("  (" + nx_seal_path() + ")"))
        if renew { print(cx_warn("  the old seal is void — temples must entrust the new print")) }
    }
    bind pub = ed25519_public(h)
    print(cx_meta("seal print  ") + cx_aura(pub) + cx_dim("  (fingerprint)"))
    print(cx_stage("  " + pub))
    print(cx_dim("  send this print to a temple keeper; they run:"))
    print(cx_dim("    miao entrust <your-name> " + ut_short(pub) + "…"))
    return 0.0
}

# .miao/entrusted lines: "name|pubhex". returns [names, pubs]
fn nx_entrusted() {
    bind lines = ut_read_lines(sx_dir() + "/entrusted")
    bind names = list_new()
    bind pubs = list_new()
    bind i = 0.0
    while i < len(lines) {
        bind ln = list_get(lines, i)
        bind sp = str_find(ln, "|")
        if sp > 0.0 {
            bind names = list_push(names, str_slice(ln, 0.0, sp))
            bind pubs = list_push(pubs, str_slice(ln, sp + 1.0, 99999.0))
        }
        bind i = i + 1.0
    }
    return [names, pubs]
}

fn nx_entrusted_name(pub) {
    bind e = nx_entrusted()
    bind at = ut_index_of(list_get(e, 1.0), pub)
    if at < 0.0 { return "" }
    return list_get(list_get(e, 0.0), at)
}

fn nx_entrust_cmd(args) {
    if len(args) < 1.0 {
        bind e = nx_entrusted()
        bind names = list_get(e, 0.0)
        bind pubs = list_get(e, 1.0)
        if len(names) < 1.0 {
            print(cx_dim("no keepers are entrusted — this temple accepts no sealed tribute"))
            print(cx_dim("  entrust one: miao entrust <name> <their seal print>"))
            return 0.0
        }
        bind i = 0.0
        while i < len(names) {
            print(cx_good("印 " + ut_pad(list_get(names, i), 16.0)) + " " + cx_aura(list_get(pubs, i)) + cx_dim(" " + list_get(pubs, i)))
            bind i = i + 1.0
        }
        return 0.0
    }
    bind first = list_get(args, 0.0)
    if first == "--revoke" || first == "-d" {
        bind who = list_get(args, 1.0)
        bind lines = ut_read_lines(sx_dir() + "/entrusted")
        bind out = ""
        bind found = false
        bind i = 0.0
        while i < len(lines) {
            bind ln = list_get(lines, i)
            if str_starts_with(ln, who + "|") {
                bind found = true
            } else {
                bind out = out + ln + "\n"
            }
            bind i = i + 1.0
        }
        write_file(sx_dir() + "/entrusted", out)
        if found {
            print(cx_warn("trust revoked: " + who + " may no longer push"))
        } else {
            print(cx_err("no entrusted keeper named " + who))
        }
        return 0.0
    }
    if len(args) < 2.0 {
        print(cx_err("usage: miao entrust <name> <seal-print>   |   miao entrust --revoke <name>"))
        return 0.0
    }
    bind pub = str_trim(list_get(args, 1.0))
    if len(pub) != 64.0 {
        print(cx_err("that is not a seal print (expected 64 hex characters, got " + len(pub) + ")"))
        return 0.0
    }
    bind lines = ut_read_lines(sx_dir() + "/entrusted")
    bind out = ""
    bind i = 0.0
    while i < len(lines) {
        bind ln = list_get(lines, i)
        if str_starts_with(ln, first + "|") == false { bind out = out + ln + "\n" }
        bind i = i + 1.0
    }
    write_file(sx_dir() + "/entrusted", out + first + "|" + pub + "\n")
    sx_log("entrust", first + "|" + ut_short(pub))
    print(cx_good("✦ entrusted: ") + cx_info(first) + cx_dim(" ") + cx_aura(pub))
    print(cx_dim("  their sealed tributes will now be accepted at every fair of this temple"))
    return 0.0
}

# shared tribute application, used by both the legacy shared-secret path
# and the sealed path. who_label is shown and chronicled.
fn nx_tribute_apply(cid, hexbundle, who_label) {
    bind tr = hex_text(hexbundle)
    match tr {
        bad(e) => {
            ssh_serve_send(cid, "ERR malformed tribute")
            ssh_serve_exit(cid, 1.0)
            return 0.0
        }
        ok(v) => { bind tr = tr }
    }
    bind text = tr.unwrap()
    bind res = nx_apply(text, "tribute", "")
    bind moved = nx_advance_souls(list_get(res, 1.0))
    ssh_serve_send(cid, "OK " + list_get(res, 0.0) + " rites received, " + moved + " soul(s) advanced — welcomed from " + who_label)
    ssh_serve_exit(cid, 0.0)
    sx_log("tribute-from", who_label + "|" + list_get(res, 0.0) + " rites")
    print(cx_good("  → tribute accepted from " + who_label + ": " + list_get(res, 0.0) + " new rite(s)"))
    return 0.0
}

# ── fair (serve over SSH) ─────────────────────────────────────────────
fn nx_fair(args) {
    bind port = 7447.0
    bind tribute = false
    bind i = 0.0
    while i < len(args) {
        bind a0 = list_get(args, i)
        if a0 == "--port" { bind port = to_number(list_get(args, i + 1.0)) }
        if a0 == "--tribute" { bind tribute = true }
        bind i = i + 1.0
    }
    bind name = path_name(sx_root())
    bind pw = random_hex(9.0)
    bind wpw = ""
    if tribute { bind wpw = random_hex(9.0) }
    make_dir(sx_dir() + "/fair")
    bind keypath = sx_dir() + "/fair/host_ed25519"
    ssh_config(port, "pilgrim", pw, keypath)
    bind okd = ssh_serve()
    if okd == false {
        print(cx_err("the temple gates would not open (port " + port + " busy?)"))
        return 0.0
    }
    bind link = "miao+ssh://pilgrim:" + pw + "@" + env_get("COMPUTERNAME", env_get("HOSTNAME", "<your-ip>")) + ":" + port + "/" + name
    print(cx_rainbow("庙会 · temple fair is open"))
    print(cx_rule(54.0))
    print(cx_meta("  temple    ") + cx_info(name))
    print(cx_meta("  gate      ") + cx_info("ssh port " + port) + cx_dim("  (its own host key — your ~/.ssh is never touched)"))
    print(cx_meta("  share     ") + cx_stage(link))
    print(cx_dim("            (replace the host with your reachable IP/hostname if needed)"))
    if tribute {
        print(cx_meta("  tribute   ") + cx_warn(link + "?tribute=" + wpw))
        print(cx_dim("            (holders of THIS link may also push — guard it)"))
    } else {
        print(cx_meta("  access    ") + cx_good("read-only — pilgrims can take, never touch"))
    }
    bind ent = nx_entrusted()
    bind ent_names = list_get(ent, 0.0)
    if len(ent_names) > 0.0 {
        print(cx_meta("  entrusted ") + cx_tal(list_join(ent_names, " · ")) + cx_dim("  (their sealed pushes are accepted)"))
    } else {
        print(cx_meta("  entrusted ") + cx_dim("no keepers — `miao entrust <name> <seal print>` to allow chosen pushers"))
    }
    bind svg = qr_svg(link)
    if svg != "" {
        write_file(sx_dir() + "/fair/link.svg", svg)
        print(cx_meta("  QR        ") + cx_dim(".miao/fair/link.svg"))
    }
    print(cx_rule(54.0))
    print(cx_dim("  the fair stays open until you close this process (ctrl+c)"))
    net_announce(7448.0, "miao|" + name + "|" + port)
    bind go = true
    while go {
        bind ev = ssh_serve_events()
        if ev != "" {
            bind evl = split(ev, "\n")
            bind k = 0.0
            while k < len(evl) {
                bind e = list_get(evl, k)
                if e != "" { print(cx_dim("  · " + e)) }
                bind k = k + 1.0
            }
        }
        bind msg = ssh_serve_recv()
        if msg == "" {
            sleep_ms(40.0)
        } else {
            bind sp = str_find(msg, "|")
            bind cid = to_number(str_slice(msg, 0.0, sp))
            bind cmd = str_trim(str_slice(msg, sp + 1.0, 99999999.0))
            nx_fair_handle(cid, cmd, name, tribute, wpw)
        }
    }
    return 0.0
}

fn nx_fair_handle(cid, cmd, name, tribute, wpw) {
    if cmd == "MIAO HELLO" {
        bind t = "TEMPLE " + name + "\n"
        bind souls = sx_souls()
        bind i = 0.0
        while i < len(souls) {
            bind tip = sx_tip(list_get(souls, i))
            if tip != "" { bind t = t + "soul " + list_get(souls, i) + " " + tip + "\n" }
            bind i = i + 1.0
        }
        ssh_serve_send(cid, t + "END")
        ssh_serve_exit(cid, 0.0)
        print(cx_info("  → a pilgrim asked after the temple"))
        return 0.0
    }
    if cmd == "MIAO BUNDLE" {
        print(cx_good("  → spirit division: sending the whole temple…"))
        ssh_serve_send(cid, nx_bundle(""))
        ssh_serve_exit(cid, 0.0)
        return 0.0
    }
    if str_starts_with(cmd, "MIAO SINCE ") {
        bind csv = str_slice(cmd, 11.0, 99999999.0)
        print(cx_good("  → pilgrimage: sending fresh incense…"))
        ssh_serve_send(cid, nx_bundle(csv))
        ssh_serve_exit(cid, 0.0)
        return 0.0
    }
    if str_starts_with(cmd, "MIAO TRIBUTE-SEALED ") {
        # sealed push: "MIAO TRIBUTE-SEALED <pub64> <sig128> <hexbundle>"
        # accepted from entrusted keepers even on a read-only fair.
        bind rest = str_slice(cmd, 20.0, 99999999.0)
        bind s1 = str_find(rest, " ")
        bind pub = str_slice(rest, 0.0, s1)
        bind rest2 = str_slice(rest, s1 + 1.0, 99999999.0)
        bind s2 = str_find(rest2, " ")
        bind sig = str_slice(rest2, 0.0, s2)
        bind hexbundle = str_slice(rest2, s2 + 1.0, 99999999.0)
        bind who = nx_entrusted_name(pub)
        if who == "" {
            ssh_serve_send(cid, "ERR this seal is not entrusted here")
            ssh_serve_exit(cid, 1.0)
            print(cx_err("  → a sealed tribute was refused (unknown seal " + ut_short(pub) + "…)"))
            return 0.0
        }
        bind bh = hash_hex(hexbundle)
        if ed25519_verify(pub, "miao-tribute|" + name + "|" + bh, sig) == false {
            ssh_serve_send(cid, "ERR the seal does not match the tribute")
            ssh_serve_exit(cid, 1.0)
            print(cx_err("  → a sealed tribute was refused (bad signature, claimed " + who + ")"))
            return 0.0
        }
        print(cx_good("  → a sealed tribute from " + who + " (seal verified)…"))
        nx_tribute_apply(cid, hexbundle, who)
        return 0.0
    }
    if str_starts_with(cmd, "MIAO TRIBUTE ") {
        if tribute == false {
            ssh_serve_send(cid, "ERR this temple does not accept tribute — ask to be entrusted (miao seal)")
            ssh_serve_exit(cid, 1.0)
            return 0.0
        }
        bind rest = str_slice(cmd, 13.0, 99999999.0)
        bind sp = str_find(rest, " ")
        bind given = str_slice(rest, 0.0, sp)
        bind hexbundle = str_slice(rest, sp + 1.0, 99999999.0)
        if hex_eq(text_hex(given), text_hex(wpw)) == false {
            ssh_serve_send(cid, "ERR wrong tribute secret")
            ssh_serve_exit(cid, 1.0)
            print(cx_err("  → a tribute was refused (wrong secret)"))
            return 0.0
        }
        nx_tribute_apply(cid, hexbundle, "the tribute link")
        return 0.0
    }
    ssh_serve_send(cid, "ERR unknown ceremony")
    ssh_serve_exit(cid, 1.0)
    return 0.0
}

# ── enshrine (clone) ──────────────────────────────────────────────────
fn nx_enshrine(args) {
    if len(args) < 1.0 {
        print(cx_err("usage: miao enshrine <link> [folder]   or   miao enshrine near"))
        return 0.0
    }
    bind link = list_get(args, 0.0)
    if link == "near" {
        print(cx_info("listening for nearby temple fairs…"))
        bind t0 = time_now()
        bind found = ""
        while time_now() - t0 < 3.0 {
            bind got = net_discover(7448.0)
            if got != "" { bind found = got }
            sleep_ms(200.0)
        }
        if found == "" {
            print(cx_dim("  no fairs within earshot"))
        } else {
            bind lines = split(found, "\n")
            bind i = 0.0
            while i < len(lines) {
                bind ln = list_get(lines, i)
                if ln != "" {
                    bind sp = str_find(ln, "|")
                    bind ip = str_slice(ln, 0.0, sp)
                    bind info = str_slice(ln, sp + 1.0, 99999.0)
                    bind parts = split(info, "|")
                    if list_get(parts, 0.0) == "miao" {
                        print("  " + cx_good("⛩ ") + cx_info(list_get(parts, 1.0)) + cx_dim(" at " + ip + ":" + list_get(parts, 2.0)) + cx_dim("  (ask the keeper for the link)"))
                    }
                }
                bind i = i + 1.0
            }
        }
        return 0.0
    }
    bind l = nx_link_parse(link)
    bind host = list_get(l, 0.0)
    if host == "" {
        print(cx_err("that link holds no power — expected miao+ssh://pilgrim:SECRET@host:port/name"))
        return 0.0
    }
    bind name = list_get(l, 4.0)
    bind dir = name
    if len(args) > 1.5 { bind dir = list_get(args, 1.0) }
    if dir == "" { bind dir = "temple" }
    if file_exists(dir + "/.miao/MIAO") {
        print(cx_err("a temple already stands at " + dir))
        return 0.0
    }
    print(cx_info("⛩ journeying to " + host + ":" + list_get(l, 1.0) + " …"))
    bind r = ssh_connect(host, list_get(l, 1.0), list_get(l, 2.0), list_get(l, 3.0))
    if r < 0.5 {
        if r < 0.0 - 1.5 {
            print(cx_err("the gate keeper has changed faces (host key mismatch) — remove .ling-ssh/known_hosts if you trust the new key"))
        } else {
            print(cx_err("the temple gates did not open (connect/auth failed)"))
        }
        return 0.0
    }
    bind text = ssh_exec("MIAO BUNDLE")
    ssh_close()
    if str_starts_with(text, "MIAOBUNDLE") == false {
        print(cx_err("the answer was not a spirit bundle"))
        return 0.0
    }
    make_dir(dir)
    sx_init(dir)
    st_set("sx_root", ut_norm(absolute_path(dir)))
    bind res = nx_apply(text, "clone", "")
    nx_mother_set("origin", link)
    # summon main (or the first soul we got)
    bind souls = sx_souls()
    bind pick = "main"
    if ut_contains(souls, "main") == false {
        if len(souls) > 0.0 { bind pick = list_get(souls, 0.0) }
    }
    sx_set_head_soul(pick)
    rt_summon_to_rite(sx_tip(pick))
    print(cx_good("✦ spirit division complete — ") + cx_info(dir) + cx_dim("  (" + list_get(res, 0.0) + " rites, soul " + pick + ")"))
    print(cx_dim("  the mother temple is remembered as ") + cx_info("origin"))
    return 0.0
}

# ── pilgrimage (pull) ─────────────────────────────────────────────────
fn nx_pilgrimage(args) {
    bind mother = "origin"
    if len(args) > 0.5 { bind mother = list_get(args, 0.0) }
    bind link = nx_mother_get(mother)
    if link == "" {
        print(cx_err("no ancestral temple named " + mother + " — see `miao mother`"))
        return 0.0
    }
    bind l = nx_link_parse(link)
    print(cx_info("⛩ pilgrimage to " + list_get(l, 0.0) + " …"))
    bind r = ssh_connect(list_get(l, 0.0), list_get(l, 1.0), list_get(l, 2.0), list_get(l, 3.0))
    if r < 0.5 {
        print(cx_err("the temple gates did not open"))
        return 0.0
    }
    # tell them which rites we already hold (our soul tips + known mother tips)
    bind haves = ""
    bind souls = sx_souls()
    bind i = 0.0
    while i < len(souls) {
        bind tip = sx_tip(list_get(souls, i))
        if tip != "" {
            if haves != "" { bind haves = haves + "," }
            bind haves = haves + tip
        }
        bind i = i + 1.0
    }
    bind text = ssh_exec("MIAO SINCE " + haves)
    ssh_close()
    if str_starts_with(text, "MIAOBUNDLE") == false {
        print(cx_err("the answer was not a spirit bundle"))
        return 0.0
    }
    bind res = nx_apply(text, "pull", mother)
    bind added = list_get(res, 0.0)
    print(cx_good("✦ incense renewed — " + added + " new rite(s)"))
    # try fast-forwarding the current soul
    bind h = sx_head()
    if list_get(h, 0.0) == "soul" {
        bind cur = list_get(h, 1.0)
        bind lines = ut_read_lines(sx_dir() + "/mtips")
        bind i = 0.0
        while i < len(lines) {
            bind parts = split(list_get(lines, i), "|")
            if list_get(parts, 0.0) == mother {
                if list_get(parts, 1.0) == cur {
                    bind theirs = list_get(parts, 2.0)
                    bind ours = sx_tip(cur)
                    if theirs == ours {
                        print(cx_good("  " + cur + " already flows with the mother temple"))
                    } else {
                        bind can_ff = false
                        if ours == "" { bind can_ff = true }
                        if can_ff == false {
                            if ut_contains(rt_ancestors(theirs), ours) { bind can_ff = true }
                        }
                        if can_ff {
                            bind d = rt_status_data()
                            bind dirty = len(list_get(d, 0.0)) + len(list_get(d, 2.0))
                            if dirty > 0.5 {
                                print(cx_warn("  new rites await, but the temple is unsettled — seal or restore, then `miao pilgrimage` again"))
                            } else {
                                rt_summon_to_rite(theirs)
                                sx_set_tip(cur, theirs)
                                print(cx_good("  ✦ " + cur + " advanced to ") + cx_aura(theirs))
                            }
                        } else {
                            print(cx_warn("  the lineages have diverged — `miao harmonize " + mother + "/" + cur + "`"))
                        }
                    }
                }
            }
            bind i = i + 1.0
        }
    }
    return 0.0
}

# ── tribute (push) ────────────────────────────────────────────────────
fn nx_tribute(args) {
    bind mother = "origin"
    if len(args) > 0.5 { bind mother = list_get(args, 0.0) }
    bind link = nx_mother_get(mother)
    if link == "" {
        print(cx_err("no ancestral temple named " + mother))
        return 0.0
    }
    bind l = nx_link_parse(link)
    bind wpw = list_get(l, 5.0)
    bind seal = 0.0 - 1.0
    if wpw == "" {
        # no shared secret in the link: push with the keeper's seal instead
        bind seal = nx_seal_handle()
        if seal < 0.0 {
            print(cx_err("this link has no tribute secret and you hold no keeper's seal"))
            print(cx_dim("  cut one with `miao seal`, send its print to the temple keeper,"))
            print(cx_dim("  and ask them to run `miao entrust <you> <print>`"))
            return 0.0
        }
        print(cx_info("⛩ carrying a sealed tribute to " + list_get(l, 0.0) + " …") + cx_dim("  (seal " + ut_short(ed25519_public(seal)) + "…)"))
    } else {
        print(cx_info("⛩ carrying tribute to " + list_get(l, 0.0) + " …"))
    }
    bind r = ssh_connect(list_get(l, 0.0), list_get(l, 1.0), list_get(l, 2.0), list_get(l, 3.0))
    if r < 0.5 {
        print(cx_err("the temple gates did not open"))
        return 0.0
    }
    # learn their tips so we only send what they lack
    bind hello = ssh_exec("MIAO HELLO")
    bind theirs = ""
    bind hl = split(hello, "\n")
    bind i = 0.0
    while i < len(hl) {
        bind ln = list_get(hl, i)
        if str_starts_with(ln, "soul ") {
            bind rest = str_slice(ln, 5.0, 99999.0)
            bind sp = str_find(rest, " ")
            if theirs != "" { bind theirs = theirs + "," }
            bind theirs = theirs + str_slice(rest, sp + 1.0, 99999.0)
        }
        bind i = i + 1.0
    }
    bind bundle = nx_bundle(theirs)
    bind hexbundle = text_hex(bundle)
    bind reply = ""
    if wpw == "" {
        bind bh = hash_hex(hexbundle)
        bind sig = ed25519_sign(seal, "miao-tribute|" + list_get(l, 4.0) + "|" + bh)
        bind reply = ssh_exec("MIAO TRIBUTE-SEALED " + ed25519_public(seal) + " " + sig + " " + hexbundle)
    } else {
        bind reply = ssh_exec("MIAO TRIBUTE " + wpw + " " + hexbundle)
    }
    ssh_close()
    if str_starts_with(reply, "OK") {
        print(cx_good("✦ tribute accepted — " + str_trim(str_slice(reply, 3.0, 99999.0))))
    } else {
        print(cx_err("tribute refused: " + reply))
    }
    return 0.0
}

# ── vessel (offline bundle export / import) ───────────────────────────
fn nx_vessel(args) {
    if len(args) < 1.0 {
        print(cx_err("usage: miao vessel <out-file>    or    miao vessel --open <file>"))
        return 0.0
    }
    if list_get(args, 0.0) == "--open" {
        bind f = list_get(args, 1.0)
        bind text = ut_read_text(f)
        if str_starts_with(text, "MIAOBUNDLE") == false {
            print(cx_err("that vessel holds no spirits"))
            return 0.0
        }
        bind res = nx_apply(text, "pull", "vessel")
        bind moved = nx_advance_souls(list_get(res, 1.0))
        print(cx_good("✦ vessel opened — " + list_get(res, 0.0) + " new rite(s), " + moved + " soul(s) advanced"))
        print(cx_dim("  see `miao lineage --all`, then `miao summon <soul>`"))
        return 0.0
    }
    bind out = list_get(args, 0.0)
    write_file(out, nx_bundle(""))
    print(cx_good("✦ the whole temple sealed into a vessel: ") + cx_info(out))
    print(cx_dim("  carry it anywhere; open with `miao vessel --open " + out + "`"))
    return 0.0
}

← 索引に戻る