name updates
authorThomas Walker Lynch <eknp9n@reasoningtechnology.com>
Mon, 3 Nov 2025 05:32:37 +0000 (05:32 +0000)
committerThomas Walker Lynch <eknp9n@reasoningtechnology.com>
Mon, 3 Nov 2025 05:32:37 +0000 (05:32 +0000)
developer/source/manager/CLI [new file with mode: 0755]
developer/source/manager/CLI.py [deleted file]
developer/source/manager/subu_bpf.py [deleted file]
developer/source/manager/subu_db.py [deleted file]
developer/source/manager/subu_net.py [deleted file]
developer/source/manager/subu_text.py [deleted file]
developer/source/manager/subu_utils.py [deleted file]
developer/source/manager/subu_version.py [deleted file]
developer/source/manager/subu_wg.py [deleted file]
developer/source/manager/subu_worker_bpf.py [deleted file]
developer/source/manager/test.sh

diff --git a/developer/source/manager/CLI b/developer/source/manager/CLI
new file mode 100755 (executable)
index 0000000..a79691e
--- /dev/null
@@ -0,0 +1,146 @@
+#!/usr/bin/env python3
+# -*- mode: python; coding: utf-8; python-indent-offset: 2; indent-tabs-mode: nil -*-
+"""
+CLI.py — thin command-line harness
+Version: 0.2.0
+"""
+import sys, argparse
+from text import USAGE, HELP, EXAMPLE, VERSION
+import core
+
+def CLI(argv=None) -> int:
+  argv = argv or sys.argv[1:]
+  if not argv:
+    print(USAGE)
+    return 0
+
+  # simple verbs that bypass argparse (so `help/version/example` always work)
+  simple = {"help": HELP, "--help": HELP, "-h": HELP, "usage": USAGE, "example": EXAMPLE, "version": VERSION}
+  if argv[0] in simple:
+    out = simple[argv[0]]
+    print(out if isinstance(out, str) else out())
+    return 0
+
+  p = argparse.ArgumentParser(prog="subu", add_help=False)
+  p.add_argument("-V", "--Version", action="store_true", help="print version")
+  sub = p.add_subparsers(dest="verb")
+
+  # init
+  ap = sub.add_parser("init")
+  ap.add_argument("token", nargs="?")
+
+  # create/list/info
+  ap = sub.add_parser("create")
+  ap.add_argument("owner")
+  ap.add_argument("name")
+
+  sub.add_parser("list")
+  ap = sub.add_parser("info"); ap.add_argument("subu_id")
+  ap = sub.add_parser("information"); ap.add_argument("subu_id")
+
+  # lo
+  ap = sub.add_parser("lo")
+  ap.add_argument("state", choices=["up","down"])
+  ap.add_argument("subu_id")
+
+  # WG
+  ap = sub.add_parser("WG")
+  ap.add_argument("verb", choices=["global","create","server_provided_public_key","info","information","up","down"])
+  ap.add_argument("arg1", nargs="?")
+  ap.add_argument("arg2", nargs="?")
+
+  # attach/detach
+  ap = sub.add_parser("attach")
+  ap.add_argument("what", choices=["WG"])
+  ap.add_argument("subu_id")
+  ap.add_argument("wg_id")
+
+  ap = sub.add_parser("detach")
+  ap.add_argument("what", choices=["WG"])
+  ap.add_argument("subu_id")
+
+  # network
+  ap = sub.add_parser("network")
+  ap.add_argument("state", choices=["up","down"])
+  ap.add_argument("subu_id")
+
+  # option
+  ap = sub.add_parser("option")
+  ap.add_argument("verb", choices=["set","get","list"])
+  ap.add_argument("subu_id")
+  ap.add_argument("name", nargs="?")
+  ap.add_argument("value", nargs="?")
+
+  # exec
+  ap = sub.add_parser("exec")
+  ap.add_argument("subu_id")
+  ap.add_argument("--", dest="cmd", nargs=argparse.REMAINDER, default=[])
+
+  ns = p.parse_args(argv)
+  if ns.Version:
+    print(VERSION); return 0
+
+  try:
+    if ns.verb == "init":
+      return core.cmd_init(ns.token)
+
+    if ns.verb == "create":
+      core.create_subu(ns.owner, ns.name); return 0
+    if ns.verb == "list":
+      core.list_subu(); return 0
+    if ns.verb in ("info","information"):
+      core.info_subu(ns.subu_id); return 0
+
+    if ns.verb == "lo":
+      core.lo_toggle(ns.subu_id, ns.state); return 0
+
+    if ns.verb == "WG":
+      v = ns.verb
+      if ns.arg1 is None and v in ("info","information"):
+        print("WG info requires WG_ID"); return 2
+      if v == "global":
+        core.wg_global(ns.arg1); return 0
+      if v == "create":
+        wid = core.wg_create(ns.arg1); print(wid); return 0
+      if v == "server_provided_public_key":
+        core.wg_set_pubkey(ns.arg1, ns.arg2); return 0
+      if v in ("info","information"):
+        core.wg_info(ns.arg1); return 0
+      if v == "up":
+        core.wg_up(ns.arg1); return 0
+      if v == "down":
+        core.wg_down(ns.arg1); return 0
+
+    if ns.verb == "attach":
+      if ns.what == "WG":
+        core.attach_wg(ns.subu_id, ns.wg_id); return 0
+
+    if ns.verb == "detach":
+      if ns.what == "WG":
+        core.detach_wg(ns.subu_id); return 0
+
+    if ns.verb == "network":
+      core.network_toggle(ns.subu_id, ns.state); return 0
+
+    if ns.verb == "option":
+      if ns.verb == "option" and ns.name is None and ns.value is None and ns.verb == "list":
+        core.option_list(ns.subu_id); return 0
+      if ns.verb == "set":
+        core.option_set(ns.subu_id, ns.name, ns.value); return 0
+      if ns.verb == "get":
+        core.option_get(ns.subu_id, ns.name); return 0
+      if ns.verb == "list":
+        core.option_list(ns.subu_id); return 0
+
+    if ns.verb == "exec":
+      if not ns.cmd:
+        print("subu exec <Subu_ID> -- <cmd> ..."); return 2
+      core.exec_in_subu(ns.subu_id, ns.cmd); return 0
+
+    print(USAGE); return 2
+  except Exception as e:
+    print(f"error: {e}")
+    return 1
+
+if __name__ == "__main__":
+  sys.exit(CLI())
diff --git a/developer/source/manager/CLI.py b/developer/source/manager/CLI.py
deleted file mode 100644 (file)
index a79691e..0000000
+++ /dev/null
@@ -1,146 +0,0 @@
-#!/usr/bin/env python3
-# -*- mode: python; coding: utf-8; python-indent-offset: 2; indent-tabs-mode: nil -*-
-"""
-CLI.py — thin command-line harness
-Version: 0.2.0
-"""
-import sys, argparse
-from text import USAGE, HELP, EXAMPLE, VERSION
-import core
-
-def CLI(argv=None) -> int:
-  argv = argv or sys.argv[1:]
-  if not argv:
-    print(USAGE)
-    return 0
-
-  # simple verbs that bypass argparse (so `help/version/example` always work)
-  simple = {"help": HELP, "--help": HELP, "-h": HELP, "usage": USAGE, "example": EXAMPLE, "version": VERSION}
-  if argv[0] in simple:
-    out = simple[argv[0]]
-    print(out if isinstance(out, str) else out())
-    return 0
-
-  p = argparse.ArgumentParser(prog="subu", add_help=False)
-  p.add_argument("-V", "--Version", action="store_true", help="print version")
-  sub = p.add_subparsers(dest="verb")
-
-  # init
-  ap = sub.add_parser("init")
-  ap.add_argument("token", nargs="?")
-
-  # create/list/info
-  ap = sub.add_parser("create")
-  ap.add_argument("owner")
-  ap.add_argument("name")
-
-  sub.add_parser("list")
-  ap = sub.add_parser("info"); ap.add_argument("subu_id")
-  ap = sub.add_parser("information"); ap.add_argument("subu_id")
-
-  # lo
-  ap = sub.add_parser("lo")
-  ap.add_argument("state", choices=["up","down"])
-  ap.add_argument("subu_id")
-
-  # WG
-  ap = sub.add_parser("WG")
-  ap.add_argument("verb", choices=["global","create","server_provided_public_key","info","information","up","down"])
-  ap.add_argument("arg1", nargs="?")
-  ap.add_argument("arg2", nargs="?")
-
-  # attach/detach
-  ap = sub.add_parser("attach")
-  ap.add_argument("what", choices=["WG"])
-  ap.add_argument("subu_id")
-  ap.add_argument("wg_id")
-
-  ap = sub.add_parser("detach")
-  ap.add_argument("what", choices=["WG"])
-  ap.add_argument("subu_id")
-
-  # network
-  ap = sub.add_parser("network")
-  ap.add_argument("state", choices=["up","down"])
-  ap.add_argument("subu_id")
-
-  # option
-  ap = sub.add_parser("option")
-  ap.add_argument("verb", choices=["set","get","list"])
-  ap.add_argument("subu_id")
-  ap.add_argument("name", nargs="?")
-  ap.add_argument("value", nargs="?")
-
-  # exec
-  ap = sub.add_parser("exec")
-  ap.add_argument("subu_id")
-  ap.add_argument("--", dest="cmd", nargs=argparse.REMAINDER, default=[])
-
-  ns = p.parse_args(argv)
-  if ns.Version:
-    print(VERSION); return 0
-
-  try:
-    if ns.verb == "init":
-      return core.cmd_init(ns.token)
-
-    if ns.verb == "create":
-      core.create_subu(ns.owner, ns.name); return 0
-    if ns.verb == "list":
-      core.list_subu(); return 0
-    if ns.verb in ("info","information"):
-      core.info_subu(ns.subu_id); return 0
-
-    if ns.verb == "lo":
-      core.lo_toggle(ns.subu_id, ns.state); return 0
-
-    if ns.verb == "WG":
-      v = ns.verb
-      if ns.arg1 is None and v in ("info","information"):
-        print("WG info requires WG_ID"); return 2
-      if v == "global":
-        core.wg_global(ns.arg1); return 0
-      if v == "create":
-        wid = core.wg_create(ns.arg1); print(wid); return 0
-      if v == "server_provided_public_key":
-        core.wg_set_pubkey(ns.arg1, ns.arg2); return 0
-      if v in ("info","information"):
-        core.wg_info(ns.arg1); return 0
-      if v == "up":
-        core.wg_up(ns.arg1); return 0
-      if v == "down":
-        core.wg_down(ns.arg1); return 0
-
-    if ns.verb == "attach":
-      if ns.what == "WG":
-        core.attach_wg(ns.subu_id, ns.wg_id); return 0
-
-    if ns.verb == "detach":
-      if ns.what == "WG":
-        core.detach_wg(ns.subu_id); return 0
-
-    if ns.verb == "network":
-      core.network_toggle(ns.subu_id, ns.state); return 0
-
-    if ns.verb == "option":
-      if ns.verb == "option" and ns.name is None and ns.value is None and ns.verb == "list":
-        core.option_list(ns.subu_id); return 0
-      if ns.verb == "set":
-        core.option_set(ns.subu_id, ns.name, ns.value); return 0
-      if ns.verb == "get":
-        core.option_get(ns.subu_id, ns.name); return 0
-      if ns.verb == "list":
-        core.option_list(ns.subu_id); return 0
-
-    if ns.verb == "exec":
-      if not ns.cmd:
-        print("subu exec <Subu_ID> -- <cmd> ..."); return 2
-      core.exec_in_subu(ns.subu_id, ns.cmd); return 0
-
-    print(USAGE); return 2
-  except Exception as e:
-    print(f"error: {e}")
-    return 1
-
-if __name__ == "__main__":
-  sys.exit(CLI())
diff --git a/developer/source/manager/subu_bpf.py b/developer/source/manager/subu_bpf.py
deleted file mode 100644 (file)
index d698a9a..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-# ===== File: subu_bpf.py =====
-#!/usr/bin/env python3
-# -*- mode: python; coding: utf-8; python-indent-offset: 2; indent-tabs-mode: nil -*-
-
-"""
-Stub for eBPF steering (cgroup/connect4+sendmsg4 hooks) to enforce sk_bound_dev_if.
-Implementation notes:
-  * We will later compile a small eBPF C program (libbpf/bpftool) that:
-    - on connect4/sendmsg4: if process UID==subu UID -> sets sk_bound_dev_if to WG ifindex
-  * For now, we provide placeholders that pretend success.
-"""
-
-import subu_utils as U
-import subu_db as DB
-
-
-def install_steer(subu_id: str, wg_ifindex: int):
-  # TODO: load BPF, attach to cgroup v2 path; store cgroup path in DB
-  DB.update_subu_cgroup(subu_id, "/sys/fs/cgroup/subu_placeholder")
-  return 0
-
-
-def remove_steer(subu_id: str):
-  # TODO: detach and unload
-  return 0
diff --git a/developer/source/manager/subu_db.py b/developer/source/manager/subu_db.py
deleted file mode 100644 (file)
index 40c5f25..0000000
+++ /dev/null
@@ -1,176 +0,0 @@
-# ===== File: subu_db.py =====
-#!/usr/bin/env python3
-# -*- mode: python; coding: utf-8; python-indent-offset: 2; indent-tabs-mode: nil -*-
-
-import os, sqlite3, json
-import subu_utils as U
-
-SCHEMA = {
-  "meta": "CREATE TABLE IF NOT EXISTS meta (k TEXT PRIMARY KEY, v TEXT)" ,
-  "subu": """
-    CREATE TABLE IF NOT EXISTS subu (
-      id INTEGER PRIMARY KEY AUTOINCREMENT,
-      masu TEXT NOT NULL,
-      subu TEXT NOT NULL,
-      uid INTEGER,
-      netns TEXT,
-      cgroup_path TEXT,
-      UNIQUE(masu, subu)
-    )""",
-  "wg": """
-    CREATE TABLE IF NOT EXISTS wg (
-      id INTEGER PRIMARY KEY AUTOINCREMENT,
-      remote TEXT NOT NULL,
-      pubkey TEXT,
-      dev TEXT,
-      addr TEXT,
-      state TEXT DEFAULT 'down'
-    )""",
-  "attach": """
-    CREATE TABLE IF NOT EXISTS attach (
-      subu_id INTEGER NOT NULL,
-      wg_id INTEGER NOT NULL,
-      PRIMARY KEY (subu_id, wg_id)
-    )""",
-}
-
-class NotInitializedError(Exception):
-  pass
-
-
-def require_initialized():
-  if not os.path.exists(U.path_db()):
-    raise NotInitializedError()
-
-
-def connect():
-  return sqlite3.connect(U.path_db())
-
-
-def init_db():
-  if os.path.exists(U.path_db()):
-    return False
-  con = sqlite3.connect(U.path_db())
-  try:
-    cur = con.cursor()
-    for sql in SCHEMA.values():
-      cur.execute(sql)
-    con.commit()
-    return True
-  finally:
-    con.close()
-
-
-def put_meta(k, v):
-  con = connect(); cur = con.cursor()
-  cur.execute("INSERT OR REPLACE INTO meta(k,v) VALUES(?,?)", (k, v))
-  con.commit(); con.close()
-
-def get_meta(k, default=None):
-  con = connect(); cur = con.cursor()
-  cur.execute("SELECT v FROM meta WHERE k=?", (k,))
-  row = cur.fetchone(); con.close()
-  return row[0] if row else default
-
-
-def create_subu(masu, subu):
-  con = connect(); cur = con.cursor()
-  cur.execute("INSERT INTO subu(masu,subu) VALUES(?,?)", (masu, subu))
-  con.commit()
-  sid = cur.lastrowid
-  con.close()
-  return f"subu_{sid}"
-
-
-def list_subu():
-  con = connect(); cur = con.cursor()
-  cur.execute("SELECT id,masu,subu,uid,netns,cgroup_path FROM subu ORDER BY id")
-  rows = cur.fetchall(); con.close(); return rows
-
-
-def subu_by_id(subu_id):
-  if not subu_id.startswith("subu_"):
-    raise ValueError("bad subu id")
-  sid = int(subu_id.split("_")[1])
-  con = connect(); cur = con.cursor()
-  cur.execute("SELECT id,masu,subu,uid,netns,cgroup_path FROM subu WHERE id=?", (sid,))
-  row = cur.fetchone(); con.close(); return row
-
-
-def update_subu_netns(subu_id, netns):
-  sid = int(subu_id.split("_")[1])
-  con = connect(); cur = con.cursor()
-  cur.execute("UPDATE subu SET netns=? WHERE id=?", (netns, sid))
-  con.commit(); con.close()
-
-
-def update_subu_uid(subu_id, uid):
-  sid = int(subu_id.split("_")[1])
-  con = connect(); cur = con.cursor()
-  cur.execute("UPDATE subu SET uid=? WHERE id=?", (uid, sid))
-  con.commit(); con.close()
-
-
-def update_subu_cgroup(subu_id, path):
-  sid = int(subu_id.split("_")[1])
-  con = connect(); cur = con.cursor()
-  cur.execute("UPDATE subu SET cgroup_path=? WHERE id=?", (path, sid))
-  con.commit(); con.close()
-
-# WG
-
-def wg_set_global_base(cidr):
-  put_meta("wg_base_cidr", cidr)
-
-
-def wg_create(remote):
-  con = connect(); cur = con.cursor()
-  cur.execute("INSERT INTO wg(remote) VALUES(?)", (remote,))
-  con.commit(); wid = cur.lastrowid; con.close(); return f"WG_{wid}"
-
-
-def wg_list():
-  con = connect(); cur = con.cursor()
-  cur.execute("SELECT id,remote,pubkey,dev,addr,state FROM wg ORDER BY id")
-  rows = cur.fetchall(); con.close(); return rows
-
-
-def wg_by_id(wg_id):
-  if not wg_id.startswith("WG_"):
-    raise ValueError("bad WG id")
-  wid = int(wg_id.split("_")[1])
-  con = connect(); cur = con.cursor()
-  cur.execute("SELECT id,remote,pubkey,dev,addr,state FROM wg WHERE id=?", (wid,))
-  row = cur.fetchone(); con.close(); return row
-
-
-def wg_update(wg_id, **kv):
-  wid = int(wg_id.split("_")[1])
-  con = connect(); cur = con.cursor()
-  cols = ",".join([f"{k}=?" for k in kv.keys()])
-  cur.execute(f"UPDATE wg SET {cols} WHERE id=?", [*kv.values(), wid])
-  con.commit(); con.close()
-
-
-def attach(subu_id, wg_id):
-  sid = int(subu_id.split("_")[1])
-  wid = int(wg_id.split("_")[1])
-  con = connect(); cur = con.cursor()
-  cur.execute("INSERT OR REPLACE INTO attach(subu_id,wg_id) VALUES(?,?)", (sid, wid))
-  con.commit(); con.close()
-
-
-def detach(subu_id, wg_id):
-  sid = int(subu_id.split("_")[1])
-  wid = int(wg_id.split("_")[1])
-  con = connect(); cur = con.cursor()
-  cur.execute("DELETE FROM attach WHERE subu_id=? AND wg_id=?", (sid, wid))
-  con.commit(); con.close()
-
-
-def attached_wg_ids(sid: int):
-  con = connect(); cur = con.cursor()
-  cur.execute("SELECT wg_id FROM attach WHERE subu_id=?", (sid,))
-  rows = [f"WG_{r[0]}" for r in cur.fetchall()]
-  con.close(); return rows
-
diff --git a/developer/source/manager/subu_net.py b/developer/source/manager/subu_net.py
deleted file mode 100644 (file)
index 86182e8..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-# ===== File: subu_net.py =====
-#!/usr/bin/env python3
-# -*- mode: python; coding: utf-8; python-indent-offset: 2; indent-tabs-mode: nil -*-
-
-import subu_utils as U
-import subu_db as DB
-
-
-def ensure_netns(ns):
-  # create netns if missing
-  rc, out = U.run("ip netns list", capture=True, check=False)
-  if ns not in out.split():
-    U.run(f"ip netns add {ns}")
-
-
-def lo(ns, action: str):
-  if action == "up":
-    U.run(f"ip -n {ns} link set lo up")
-  else:
-    U.run(f"ip -n {ns} link set lo down")
-
-
-def cmd_lo(sid, action) -> int:
-  row = DB.subu_by_id(sid)
-  if not row:
-    return U.err("unknown subu id")
-  ns = row[4] or sid
-  ensure_netns(ns)
-  lo(ns, action)
-  return 0
-
-
-def cmd_network_up(sid) -> int:
-  row = DB.subu_by_id(sid)
-  if not row:
-    return U.err("unknown subu id")
-  ns = row[4] or sid
-  ensure_netns(ns)
-  lo(ns, "up")
-  # bring all attached WG up
-  sid_int = int(sid.split("_")[1])
-  for wid in DB.attached_wg_ids(sid_int):
-    import subu_wg as WG
-    WG.cmd_wg_up(wid)
-  return U.ok(f"network up for {sid}")
-
-
-def cmd_network_down(sid) -> int:
-  row = DB.subu_by_id(sid)
-  if not row:
-    return U.err("unknown subu id")
-  ns = row[4] or sid
-  # bring attached WG down first
-  sid_int = int(sid.split("_")[1])
-  for wid in DB.attached_wg_ids(sid_int):
-    import subu_wg as WG
-    WG.cmd_wg_down(wid)
-  # leave lo state alone per spec (no warning here)
-  return U.ok(f"network down for {sid}")
-
diff --git a/developer/source/manager/subu_text.py b/developer/source/manager/subu_text.py
deleted file mode 100644 (file)
index 88434e4..0000000
+++ /dev/null
@@ -1,152 +0,0 @@
-# -*- mode: python; coding: utf-8; python-indent-offset: 2; indent-tabs-mode: nil -*-
-
-USAGE = """\
-usage: subu [-V] <verb> [<args>]
-
-Quick verbs:
-  usage                  Show this usage summary
-  help [topic]           Detailed help; same as -h / --help
-  example                End-to-end example session
-  version                Print version
-
-Main verbs:
-  init                   Initialize a new subu database (refuses if it exists)
-  create                 Create a minimal subu record (defaults only)
-  info | information     Show details for a subu
-  WG                     WireGuard object operations
-  attach                 Attach a WG object to a subu (netns + install steering)
-  detach                 Detach WG from a subu (remove steering)
-  network                Bring attached ifaces up/down inside the subu netns
-  lo                     Bring loopback up/down inside the subu netns
-  option                 Persisted options (list/set/get for future policy)
-  exec                   Run a command inside the subu netns
-
-Tip: `subu help` (or `subu --help`) shows detailed help; `subu help WG` shows topic help.
-"""
-
-HELP = """\
-subu — manage subu containers, namespaces, and WG attachments
-
-2.1 Core
-
-  subu init <TOKEN>
-      Create ./subu.db (tables: subu, wg, links, options, state).
-      Requires a 6-char token (e.g., dzkq7b). Refuses if DB already exists.
-
-  subu create <masu> <subu>
-      Make a default subu with netns ns-<Subu_ID> containing lo only (down).
-      Returns subu_N.
-
-  subu list
-      Columns: Subu_ID, Owner, Name, NetNS, WG_Attached?, Up/Down.
-
-  subu info <Subu_ID>    | subu information <Subu_ID>
-      Full record + attached WG(s) + options + iface states.
-
-2.2 Loopback
-
-  subu lo up <Subu_ID>   | subu lo down <Subu_ID>
-      Toggle loopback inside the subu’s netns.
-
-2.3 WireGuard objects (independent)
-
-  subu WG global <BaseCIDR>
-      e.g., 192.168.112.0/24; allocator hands out /32 peers sequentially.
-      Shows current base and next free on success.
-
-  subu WG create <host:port>
-      Creates WG object; allocates next /32 local IP; AllowedIPs=0.0.0.0/0.
-      Returns WG_M.
-
-  subu WG server_provided_public_key <WG_ID> <Base64Key>
-      Stores server’s pubkey.
-
-  subu WG info <WG_ID>   | subu WG information <WG_ID>
-      Endpoint, allocated IP, pubkey set?, link state (admin/oper).
-
-2.4 Link WG ↔ subu, bring up/down
-
-  subu attach WG <Subu_ID> <WG_ID>
-      Creates/configures WG device inside ns-<Subu_ID>:
-        - device name: subu_<M> (M from WG_ID)
-        - set local /32, MTU 1420, accept_local=1
-        - no default route is added
-        - installs eBPF steering (force egress via this device) automatically
-
-  subu detach WG <Subu_ID>
-      Remove WG device/config from the subu’s netns and remove steering; keep WG object.
-
-  subu WG up <WG_ID>     | subu WG down <WG_ID>
-      Toggle interface admin state in the subu’s netns (must be attached).
-      On “up”, warn if loopback is currently down in that netns.
-
-  subu network up <Subu_ID> | subu network down <Subu_ID>
-      Only toggles admin state for all attached ifaces. On “up”, loopback
-      is brought up first automatically. No route manipulation.
-
-2.5 Execution
-
-  subu exec <Subu_ID> -- <cmd> …
-      Run a process inside the subu’s netns.
-
-2.6 Options (persist only, for future policy)
-
-  subu option list <Subu_ID>
-  subu option get  <Subu_ID> [name]
-  subu option set  <Subu_ID> <name> <value>
-
-2.7 Meta
-
-  subu usage
-      Short usage summary (also printed when no args are given).
-
-  subu help [topic]
-      This help (or per-topic help such as `subu help WG`).
-
-  subu example
-      A concrete end-to-end scenario.
-
-  subu version
-      Print version (same as -V / --version).
-"""
-
-EXAMPLE = """\
-# 0) Safe init (refuses if ./subu.db exists)
-subu init dzkq7b
-# -> created ./subu.db
-
-# 1) Create Subu
-subu create Thomas US
-# -> Subu_ID: subu_7
-# -> netns: ns-subu_7 with lo (down)
-
-# 2) Define WG pool (once per host)
-subu WG global 192.168.112.0/24
-# -> base set; next free: 192.168.112.2/32
-
-# 3) Create WG object with endpoint
-subu WG create ReasoningTechnology.com:51820
-# -> WG_ID: WG_0
-# -> local IP: 192.168.112.2/32
-# -> AllowedIPs: 0.0.0.0/0
-
-# 4) Add server public key
-subu WG server_provided_public_key WG_0 ABCDEFG...xyz=
-# -> saved
-
-# 5) Attach WG to Subu (device created/configured in ns + steering)
-subu attach WG subu_7 WG_0
-# -> device ns-subu_7/subu_0 configured (no default route)
-# -> steering installed: egress forced via subu_0
-
-# 6) Bring network up (lo first, then attached ifaces)
-subu network up subu_7
-# -> lo up; subu_0 admin up
-
-# 7) Start the WG engine inside the netns
-subu WG up WG_0
-# -> up, handshakes should start (warn if lo was down)
-
-# 8) Test from inside the subu
-subu exec subu_7 -- curl -4v https://ifconfig.me
-"""
diff --git a/developer/source/manager/subu_utils.py b/developer/source/manager/subu_utils.py
deleted file mode 100644 (file)
index 3594c1c..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-# ===== File: subu_utils.py =====
-#!/usr/bin/env python3
-# -*- mode: python; coding: utf-8; python-indent-offset: 2; indent-tabs-mode: nil -*-
-
-import os, sys, subprocess, shlex
-
-
-def ok(msg: str, code: int = 0) -> int:
-  print(msg)
-  return code
-
-def err(msg: str, code: int = 2) -> int:
-  print(f"❌ {msg}")
-  return code
-
-
-def run(cmd: str, check=True, capture=False, env=None, ns_enter=None):
-  """Run shell command. If ns_enter is a netns name, prefix with `ip netns exec`.
-  Returns (rc, outstr).
-  """
-  if ns_enter:
-    cmd = f"ip netns exec {shlex.quote(ns_enter)} {cmd}"
-  p = subprocess.run(cmd, shell=True, env=env,
-                     stdout=subprocess.PIPE if capture else None,
-                     stderr=subprocess.STDOUT)
-  out = p.stdout.decode() if p.stdout else ""
-  if check and p.returncode != 0:
-    raise RuntimeError(f"command failed ({p.returncode}): {cmd}\n{out}")
-  return p.returncode, out
-
-
-def path_db():
-  return os.path.abspath("subu.db")
-
diff --git a/developer/source/manager/subu_version.py b/developer/source/manager/subu_version.py
deleted file mode 100644 (file)
index bccd1dd..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-# -*- mode: python; coding: utf-8; python-indent-offset: 2; indent-tabs-mode: nil -*-
-VERSION = "0.1.6"
diff --git a/developer/source/manager/subu_wg.py b/developer/source/manager/subu_wg.py
deleted file mode 100644 (file)
index 0eca02c..0000000
+++ /dev/null
@@ -1,139 +0,0 @@
-# ===== File: subu_wg.py =====
-#!/usr/bin/env python3
-# -*- mode: python; coding: utf-8; python-indent-offset: 2; indent-tabs-mode: nil -*-
-
-import ipaddress
-import subu_utils as U
-import subu_db as DB
-
-PREFIX_DEV = "subu_"  # device name base; dev = f"{PREFIX_DEV}{WG_id_num}"
-
-
-def cmd_wg_help() -> int:
-  print("WG commands: global <cidr> | create <host:port> | info | server_provided_public_key <WG_id> <key> | up <WG_id> | down <WG_id>")
-  return 0
-
-
-def cmd_wg_global(base_cidr: str) -> int:
-  # validate CIDR
-  try:
-    net = ipaddress.ip_network(base_cidr, strict=False)
-    if net.version != 4:
-      return U.err("only IPv4 supported for WG base")
-  except Exception as e:
-    return U.err(f"invalid cidr: {e}")
-  DB.wg_set_global_base(base_cidr)
-  return U.ok(f"WG base set to {base_cidr}")
-
-
-def allocate_addr_for(wg_id: str) -> str:
-  base = DB.get_meta("wg_base_cidr")
-  if not base:
-    raise RuntimeError("WG base not set; run 'subu WG global <cidr>'")
-  net = ipaddress.ip_network(base, strict=False)
-  wid = int(wg_id.split("_")[1])
-  host = list(net.hosts())[wid + 1]  # skip .1 for potential gateway
-  return f"{host}/32"
-
-
-def ensure_device(wg_id: str):
-  # create device if missing and store dev+addr in DB
-  row = DB.wg_by_id(wg_id)
-  if not row:
-    raise RuntimeError("unknown WG id")
-  _, remote, pubkey, dev, addr, state = row
-  if not dev:
-    dev = f"{PREFIX_DEV}{wg_id.split('_')[1]}"
-    DB.wg_update(wg_id, dev=dev)
-  if not addr:
-    addr = allocate_addr_for(wg_id)
-    DB.wg_update(wg_id, addr=addr)
-  # ensure link exists in root netns
-  rc, out = U.run("ip link show", capture=True, check=False)
-  if f": {dev}:" not in out:
-    # create WG link skeleton; full wg config is deferred
-    U.run(f"ip link add {dev} type wireguard")
-    U.run(f"ip addr add {addr} dev {dev}")
-  return dev, addr
-
-
-def move_to_netns(wg_id: str, ns: str):
-  dev, _ = ensure_device(wg_id)
-  # if already in ns, ip will refuse — treat as ok
-  U.run(f"ip link set {dev} netns {ns}", check=False)
-
-
-def detach_device(wg_id: str):
-  row = DB.wg_by_id(wg_id)
-  if not row: return
-  dev = row[3]
-  if not dev: return
-  # best effort delete (must run either in owning ns or root if present there)
-  # try root first
-  rc, out = U.run(f"ip link del {dev}", check=False)
-  if rc != 0:
-    # try to find owning ns? (skipped for brevity)
-    pass
-
-
-def cmd_wg_create(remote: str) -> int:
-  wid = DB.wg_create(remote)
-  print(wid)
-  return 0
-
-
-def cmd_wg_info() -> int:
-  rows = DB.wg_list()
-  for wid, remote, pubkey, dev, addr, state in rows:
-    print(f"WG_{wid}: remote={remote} dev={dev} addr={addr} state={state} pubkey={'set' if pubkey else 'unset'}")
-  return 0
-
-
-def cmd_wg_set_server_pub(wg_id: str, pub: str) -> int:
-  DB.wg_update(wg_id, pubkey=pub)
-  return U.ok(f"Set server pubkey for {wg_id}")
-
-
-def _ns_of_wg(wg_id: str):
-  # discover netns from attachment
-  rows = DB.list_subu()
-  for sid, *_ in rows:
-    attached = DB.attached_wg_ids(sid)
-    if wg_id in attached:
-      row = DB.subu_by_id(f"subu_{sid}")
-      return row[4] or f"subu_{sid}"
-  return None
-
-
-def cmd_wg_up(wg_id: str) -> int:
-  row = DB.wg_by_id(wg_id)
-  if not row:
-    return U.err("unknown WG id")
-  dev, addr = ensure_device(wg_id)
-  ns = _ns_of_wg(wg_id)
-  if ns:
-    # bring lo up silently before bringing WG up
-    U.run(f"ip -n {ns} link set lo up", check=False)
-    U.run(f"ip -n {ns} link set {dev} up")
-  else:
-    U.run(f"ip link set {dev} up")
-  DB.wg_update(wg_id, state="up")
-  return U.ok(f"WG {wg_id} up")
-
-
-def cmd_wg_down(wg_id: str) -> int:
-  row = DB.wg_by_id(wg_id)
-  if not row:
-    return U.err("unknown WG id")
-  dev = row[3]
-  if not dev:
-    return U.err("WG device not created yet")
-  ns = _ns_of_wg(wg_id)
-  if ns:
-    U.run(f"ip -n {ns} link set {dev} down", check=False)
-  else:
-    U.run(f"ip link set {dev} down", check=False)
-  DB.wg_update(wg_id, state="down")
-  return U.ok(f"WG {wg_id} down")
-
-
diff --git a/developer/source/manager/subu_worker_bpf.py b/developer/source/manager/subu_worker_bpf.py
deleted file mode 100644 (file)
index 0c71d78..0000000
+++ /dev/null
@@ -1,195 +0,0 @@
-# -*- mode: python; coding: utf-8; python-indent-offset: 2; indent-tabs-mode: nil -*-
-"""
-subu_worker_bpf.py — build, load, and manage eBPF steering for a Subu
-
-What it does:
-  * Compiles subu_bpf_force_egress.c -> /var/lib/subu/bpf/subu_force_egress.bpf.o
-  * Creates pins under /sys/fs/bpf/subu/<Subu_ID>:
-        force_connect4, force_sendmsg4, force_ifindex_map
-  * Creates a cgroup v2 node at /sys/fs/cgroup/subu/<Subu_ID>
-  * Attaches programs (connect4, sendmsg4) to that cgroup
-  * Writes the target ifindex into map[0]
-  * Idempotent: re-running updates ifindex and ensures attachments
-
-Requirements:
-  * bpffs mounted at /sys/fs/bpf
-  * cgroup v2 mounted at /sys/fs/cgroup
-  * tools: clang, bpftool
-  * privileges: CAP_BPF + CAP_SYS_ADMIN
-"""
-
-import os
-import shutil
-import subprocess
-from pathlib import Path
-from typing import Dict
-
-BPF_SRC = Path("subu_bpf_force_egress.c")
-BUILD_DIR = Path("/var/lib/subu/bpf")
-BPFFS_DIR = Path("/sys/fs/bpf")
-BPF_PIN_BASE = BPFFS_DIR / "subu"            # /sys/fs/bpf/subu/<Subu_ID>/*
-CGROOT = Path("/sys/fs/cgroup/subu")         # /sys/fs/cgroup/subu/<Subu_ID>
-
-OBJ_NAME = "subu_force_egress.bpf.o"
-PROG_CONNECT_PIN = "force_connect4"
-PROG_SENDMSG_PIN = "force_sendmsg4"
-MAP_IFINDEX_PIN   = "force_ifindex_map"      # matches map name in C
-
-class BpfError(RuntimeError):
-  pass
-
-def _run(cmd, check=True):
-  r = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
-  if check and r.returncode != 0:
-    raise BpfError(f"cmd failed: {' '.join(cmd)}\nstdout:\n{r.stdout}\nstderr:\n{r.stderr}")
-  return r
-
-def _which_or_die(tool: str):
-  if not shutil.which(tool):
-    raise BpfError(f"Missing required tool: {tool}")
-
-def ensure_prereqs():
-  _which_or_die("clang")
-  _which_or_die("bpftool")
-  # bpffs?
-  if not BPFFS_DIR.exists():
-    raise BpfError(f"{BPFFS_DIR} not mounted; try: mount -t bpf bpf {BPFFS_DIR}")
-  # cgroup v2?
-  cgroot = Path("/sys/fs/cgroup")
-  if not (cgroot / "cgroup.controllers").exists():
-    raise BpfError("cgroup v2 not mounted; e.g.: mount -t cgroup2 none /sys/fs/cgroup")
-
-def ensure_dirs(subu_id: str) -> Dict[str, Path]:
-  BUILD_DIR.mkdir(parents=True, exist_ok=True)
-  (BPF_PIN_BASE).mkdir(parents=True, exist_ok=True)
-  pin_dir = BPF_PIN_BASE / subu_id
-  pin_dir.mkdir(parents=True, exist_ok=True)
-
-  CGROOT.mkdir(parents=True, exist_ok=True)
-  cgdir = CGROOT / subu_id
-  cgdir.mkdir(parents=True, exist_ok=True)
-
-  return {
-    "build_obj": BUILD_DIR / OBJ_NAME,
-    "pin_dir": pin_dir,
-    "pin_prog_connect": pin_dir / PROG_CONNECT_PIN,
-    "pin_prog_sendmsg": pin_dir / PROG_SENDMSG_PIN,
-    "pin_map_ifindex": pin_dir / MAP_IFINDEX_PIN,
-    "cgdir": cgdir,
-  }
-
-def compile_bpf(obj_path: Path):
-  if not BPF_SRC.exists():
-    raise BpfError(f"BPF source not found: {BPF_SRC}")
-  cmd = [
-    "clang", "-O2", "-g",
-    "-target", "bpf",
-    "-D__TARGET_ARCH_x86",
-    "-c", str(BPF_SRC),
-    "-o", str(obj_path),
-  ]
-  _run(cmd)
-
-def _load_prog_with_pinmaps(obj: Path, section: str, prog_pin: Path, maps_dir: Path):
-  # bpftool prog load OBJ PIN_PATH section <section> pinmaps <maps_dir>
-  _run([
-    "bpftool", "prog", "load",
-    str(obj), str(prog_pin),
-    "section", section,
-    "pinmaps", str(maps_dir),
-  ])
-
-def load_and_pin_all(p: Dict[str, Path]):
-  obj = p["build_obj"]
-  maps_dir = p["pin_dir"]
-  # Load two sections; maps get pinned into maps_dir once (first load).
-  _load_prog_with_pinmaps(obj, "cgroup/connect4", p["pin_prog_connect"], maps_dir)
-  _load_prog_with_pinmaps(obj, "cgroup/sendmsg4", p["pin_prog_sendmsg"], maps_dir)
-
-  # Ensure map exists where we expect it (pinned by name from C)
-  if not p["pin_map_ifindex"].exists():
-    # Some bpftool/libbpf combos pin maps directly as <maps_dir>/<map_name>.
-    # If not present, try to locate by name and pin.
-    # Find map id by name:
-    out = _run(["bpftool", "map", "show"]).stdout.splitlines()
-    target_id = None
-    for line in out:
-      # sample: "123: array  name force_ifindex_map  flags 0x0 ..."
-      if " name " + MAP_IFINDEX_PIN + " " in line:
-        # id is before colon
-        try:
-          target_id = line.strip().split(":", 1)[0]
-          int(target_id)  # validate
-          break
-        except Exception:
-          pass
-    if not target_id:
-      raise BpfError(f"Unable to find map '{MAP_IFINDEX_PIN}' to pin")
-    _run(["bpftool", "map", "pin", "id", target_id, str(p["pin_map_ifindex"])])
-
-def attach_to_cgroup(p: Dict[str, Path]):
-  # Attach programs to the subu-specific cgroup
-  _run(["bpftool", "cgroup", "attach", str(p["cgdir"]), "connect4", "pinned", str(p["pin_prog_connect"])])
-  _run(["bpftool", "cgroup", "attach", str(p["cgdir"]), "sendmsg4", "pinned", str(p["pin_prog_sendmsg"])])
-
-def detach_from_cgroup(p: Dict[str, Path]):
-  _run(["bpftool", "cgroup", "detach", str(p["cgdir"]), "connect4"], check=False)
-  _run(["bpftool", "cgroup", "detach", str(p["cgdir"]), "sendmsg4"], check=False)
-
-def set_ifindex(p: Dict[str, Path], ifindex: int):
-  # bpftool map update pinned <map> key <00 00 00 00> value <ifindex_le>
-  key_hex = "00 00 00 00".split()
-  val_hex = ifindex.to_bytes(4, "little").hex(" ").split()
-  _run(["bpftool", "map", "update", "pinned", str(p["pin_map_ifindex"]), "key", *key_hex, "value", *val_hex])
-
-def _ifindex_in_netns(netns_name: str, ifname: str) -> int:
-  # ip -n <ns> -o link show <ifname> -> "7: subu_0: <...>"
-  r = _run(["ip", "-n", netns_name, "-o", "link", "show", ifname])
-  first = r.stdout.strip().split(":", 1)[0]
-  return int(first)
-
-def install_steering(subu_id: str, netns_name: str, wg_ifname: str):
-  """
-  Build/load eBPF programs, pin them under /sys/fs/bpf/subu/<subu_id>,
-  attach to /sys/fs/cgroup/subu/<subu_id>, and set map[0]=ifindex(of wg_ifname in netns).
-  Idempotent across repeated calls.
-  """
-  ensure_prereqs()
-  paths = ensure_dirs(subu_id)
-  # compile if missing or stale
-  if (not paths["build_obj"].exists()) or (paths["build_obj"].stat().st_mtime < BPF_SRC.stat().st_mtime):
-    compile_bpf(paths["build_obj"])
-
-  # if pins already exist, keep them and just ensure attached + value updated
-  pins_exist = all(paths[k].exists() for k in ("pin_prog_connect", "pin_prog_sendmsg"))
-  if not pins_exist:
-    load_and_pin_all(paths)
-
-  # compute ifindex inside the subu netns
-  ifindex = _ifindex_in_netns(netns_name, wg_ifname)
-  set_ifindex(paths, ifindex)
-
-  # ensure cgroup attachments in place
-  attach_to_cgroup(paths)
-
-def remove_steering(subu_id: str):
-  """
-  Detach cgroup hooks and unpin programs/maps. Leaves the cgroup dir.
-  """
-  ensure_prereqs()
-  paths = ensure_dirs(subu_id)
-  # detach (ignore failure)
-  detach_from_cgroup(paths)
-  # unpin objects
-  for key in ("pin_prog_connect", "pin_prog_sendmsg", "pin_map_ifindex"):
-    try:
-      p = paths[key]
-      if p.exists():
-        p.unlink()
-    except Exception:
-      pass
-  # try to remove empty pin dir
-  try:
-    paths["pin_dir"].rmdir()
-  except Exception:
-    pass
index b5960f4..706250b 100644 (file)
@@ -1,13 +1,13 @@
 #!/bin/env bash
 
 set -x
-./subu.py                 # -> USAGE (exit 0)
-./subu.py usage           # -> USAGE
-./subu.py -h              # -> HELP
-./subu.py --help          # -> HELP
-./subu.py help            # -> HELP
-./subu.py help WG         # -> WG topic help (or full HELP if topic unknown)
-./subu.py example         # -> EXAMPLE
-./subu.py version         # -> 0.1.4
-./subu.py -V              # -> 0.1.4
+./CLI                 # -> USAGE (exit 0)
+./CLI usage           # -> USAGE
+./CLI -h              # -> HELP
+./CLI --help          # -> HELP
+./CLI help            # -> HELP
+./CLI help WG         # -> WG topic help (or full HELP if topic unknown)
+./CLI example         # -> EXAMPLE
+./CLI version         # -> 0.1.4
+./CLI -V              # -> 0.1.4