renaming modules
authorThomas Walker Lynch <eknp9n@reasoningtechnology.com>
Mon, 3 Nov 2025 02:41:22 +0000 (02:41 +0000)
committerThomas Walker Lynch <eknp9n@reasoningtechnology.com>
Mon, 3 Nov 2025 02:41:22 +0000 (02:41 +0000)
developer/source/manager.tgz [new file with mode: 0644]
developer/source/manager/CLI.py [new file with mode: 0644]
developer/source/manager/bpf_force_egress.c [new file with mode: 0644]
developer/source/manager/core.py [new file with mode: 0644]
developer/source/manager/subu.py [deleted file]
developer/source/manager/subu_BPF_force_egress.c [deleted file]
developer/source/manager/subu_core.py [deleted file]
developer/source/manager/temp.sh [new file with mode: 0644]
developer/source/manager/text.py [new file with mode: 0644]
developer/source/manager/worker_bpf.py [new file with mode: 0644]

diff --git a/developer/source/manager.tgz b/developer/source/manager.tgz
new file mode 100644 (file)
index 0000000..d585797
Binary files /dev/null and b/developer/source/manager.tgz differ
diff --git a/developer/source/manager/CLI.py b/developer/source/manager/CLI.py
new file mode 100644 (file)
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/bpf_force_egress.c b/developer/source/manager/bpf_force_egress.c
new file mode 100644 (file)
index 0000000..c3aedec
--- /dev/null
@@ -0,0 +1,43 @@
+// -*- mode: c; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*-
+// bpf_force_egress.c — MVP scaffold to validate UID and prep metadata
+// Version 0.2.0
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_endian.h>
+
+char LICENSE[] SEC("license") = "GPL";
+
+struct {
+  __uint(type, BPF_MAP_TYPE_HASH);
+  __type(key, __u32);         // tgid
+  __type(value, __u32);       // reserved (target ifindex placeholder)
+  __uint(max_entries, 1024);
+} subu_tgid2if SEC(".maps");
+
+// Helper: return 0 = allow, <0 reject
+static __always_inline int allow_uid(struct bpf_sock_addr *ctx) {
+  // MVP: just accept everyone; you can gate on UID 2017 with bpf_get_current_uid_gid()
+  // __u32 uid = (__u32)(bpf_get_current_uid_gid() & 0xffffffff);
+  // if (uid != 2017) return -1;
+  return 0;
+}
+
+// Hook: cgroup/connect4 — runs before connect(2) proceeds
+SEC("cgroup/connect4")
+int subu_connect4(struct bpf_sock_addr *ctx)
+{
+  if (allow_uid(ctx) < 0) return -1;
+  // Future: read pinned map/meta, set SO_* via bpf_setsockopt when permitted
+  return 0;
+}
+
+// Hook: cgroup/post_bind4 — runs after a local bind is chosen
+SEC("cgroup/post_bind4")
+int subu_post_bind4(struct bpf_sock *sk)
+{
+  // Future: enforce bound dev if kernel helper allows; record tgid->ifindex
+  __u32 tgid = bpf_get_current_pid_tgid() >> 32;
+  __u32 val = 0;
+  bpf_map_update_elem(&subu_tgid2if, &tgid, &val, BPF_ANY);
+  return 0;
+}
diff --git a/developer/source/manager/core.py b/developer/source/manager/core.py
new file mode 100644 (file)
index 0000000..c363ec2
--- /dev/null
@@ -0,0 +1,254 @@
+# -*- mode: python; coding: utf-8; python-indent-offset: 2; indent-tabs-mode: nil -*-
+"""
+core.py — worker API for subu manager
+Version: 0.2.0
+"""
+import os, sqlite3, subprocess
+from pathlib import Path
+from contextlib import closing
+from text import VERSION
+from worker_bpf import ensure_mounts, install_steering, remove_steering, BpfError
+
+DB_FILE = Path("./subu.db")
+WG_GLOBAL_FILE = Path("./WG_GLOBAL")
+
+def run(cmd, check=True):
+  r = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
+  if check and r.returncode != 0:
+    raise RuntimeError(f"cmd failed: {' '.join(cmd)}\n{r.stderr}")
+  return r.stdout.strip()
+
+# ---------------- DB ----------------
+def _db():
+  if not DB_FILE.exists():
+    raise FileNotFoundError("subu.db not found; run `subu init <token>` first")
+  return sqlite3.connect(DB_FILE)
+
+def cmd_init(token: str|None):
+  if DB_FILE.exists():
+    raise FileExistsError("db already exists")
+  if not token or len(token) < 6:
+    raise ValueError("init requires a 6+ char token")
+  with closing(sqlite3.connect(DB_FILE)) as db:
+    c = db.cursor()
+    c.executescript("""
+      CREATE TABLE subu (
+        id INTEGER PRIMARY KEY AUTOINCREMENT,
+        owner TEXT,
+        name TEXT,
+        netns TEXT,
+        lo_state TEXT DEFAULT 'down',
+        wg_id INTEGER,
+        network_state TEXT DEFAULT 'down'
+      );
+      CREATE TABLE wg (
+        id INTEGER PRIMARY KEY AUTOINCREMENT,
+        endpoint TEXT,
+        local_ip TEXT,
+        allowed_ips TEXT,
+        pubkey TEXT,
+        state TEXT DEFAULT 'down'
+      );
+      CREATE TABLE options (
+        subu_id INTEGER,
+        name TEXT,
+        value TEXT,
+        PRIMARY KEY (subu_id, name)
+      );
+    """)
+    db.commit()
+  print(f"created subu.db (v{VERSION})")
+
+# ------------- Subu ops -------------
+def create_subu(owner: str, name: str) -> str:
+  with closing(_db()) as db:
+    c = db.cursor()
+    subu_netns = f"ns-subu_tmp"  # temp; we rename after ID known
+    c.execute("INSERT INTO subu (owner, name, netns) VALUES (?, ?, ?)",
+              (owner, name, subu_netns))
+    sid = c.lastrowid
+    netns = f"ns-subu_{sid}"
+    c.execute("UPDATE subu SET netns=? WHERE id=?", (netns, sid))
+    db.commit()
+
+  # create netns
+  run(["ip", "netns", "add", netns])
+  run(["ip", "-n", netns, "link", "set", "lo", "down"])
+  print(f"Created subu_{sid} ({owner}:{name}) with netns {netns}")
+  return f"subu_{sid}"
+
+def list_subu():
+  with closing(_db()) as db:
+    for row in db.execute("SELECT id, owner, name, netns, lo_state, wg_id, network_state FROM subu"):
+      print(row)
+
+def info_subu(subu_id: str):
+  sid = int(subu_id.split("_")[1])
+  with closing(_db()) as db:
+    row = db.execute("SELECT * FROM subu WHERE id=?", (sid,)).fetchone()
+    if not row:
+      print("not found"); return
+    print(row)
+    wg = db.execute("SELECT wg_id FROM subu WHERE id=?", (sid,)).fetchone()[0]
+    if wg is not None:
+      wrow = db.execute("SELECT * FROM wg WHERE id=?", (wg,)).fetchone()
+      print("WG:", wrow)
+    opts = db.execute("SELECT name,value FROM options WHERE subu_id=?", (sid,)).fetchall()
+    print("Options:", opts)
+
+def lo_toggle(subu_id: str, state: str):
+  sid = int(subu_id.split("_")[1])
+  with closing(_db()) as db:
+    ns = db.execute("SELECT netns FROM subu WHERE id=?", (sid,)).fetchone()
+    if not ns: raise ValueError("subu not found")
+    ns = ns[0]
+    run(["ip", "netns", "exec", ns, "ip", "link", "set", "lo", state])
+    db.execute("UPDATE subu SET lo_state=? WHERE id=?", (state, sid))
+    db.commit()
+  print(f"{subu_id}: lo {state}")
+
+# ------------- WG ops ---------------
+def wg_global(basecidr: str):
+  WG_GLOBAL_FILE.write_text(basecidr.strip()+"\n")
+  print(f"WG pool base = {basecidr}")
+
+def _alloc_ip(idx: int, base: str) -> str:
+  # simplistic /24 allocator: base must be x.y.z.0/24
+  prefix = base.split("/")[0].rsplit(".", 1)[0]
+  host = 2 + idx
+  return f"{prefix}.{host}/32"
+
+def wg_create(endpoint: str) -> str:
+  if not WG_GLOBAL_FILE.exists():
+    raise RuntimeError("set WG base with `subu WG global <CIDR>` first")
+  base = WG_GLOBAL_FILE.read_text().strip()
+  with closing(_db()) as db:
+    c = db.cursor()
+    idx = c.execute("SELECT COUNT(*) FROM wg").fetchone()[0]
+    local_ip = _alloc_ip(idx, base)
+    c.execute("INSERT INTO wg (endpoint, local_ip, allowed_ips) VALUES (?, ?, ?)",
+              (endpoint, local_ip, "0.0.0.0/0"))
+    wid = c.lastrowid
+    db.commit()
+  print(f"WG_{wid} endpoint={endpoint} ip={local_ip}")
+  return f"WG_{wid}"
+
+def wg_set_pubkey(wg_id: str, key: str):
+  wid = int(wg_id.split("_")[1])
+  with closing(_db()) as db:
+    db.execute("UPDATE wg SET pubkey=? WHERE id=?", (key, wid))
+    db.commit()
+  print("ok")
+
+def wg_info(wg_id: str):
+  wid = int(wg_id.split("_")[1])
+  with closing(_db()) as db:
+    row = db.execute("SELECT * FROM wg WHERE id=?", (wid,)).fetchone()
+    print(row if row else "not found")
+
+def wg_up(wg_id: str):
+  wid = int(wg_id.split("_")[1])
+  # Admin-up of WG device handled via network_toggle once attached.
+  print(f"{wg_id}: up (noop until attached)")
+
+def wg_down(wg_id: str):
+  wid = int(wg_id.split("_")[1])
+  print(f"{wg_id}: down (noop until attached)")
+
+# ---------- attach/detach + BPF ----------
+def attach_wg(subu_id: str, wg_id: str):
+  ensure_mounts()
+  sid = int(subu_id.split("_")[1]); wid = int(wg_id.split("_")[1])
+  with closing(_db()) as db:
+    r = db.execute("SELECT netns FROM subu WHERE id=?", (sid,)).fetchone()
+    if not r: raise ValueError("subu not found")
+    ns = r[0]
+    w = db.execute("SELECT endpoint, local_ip, pubkey FROM wg WHERE id=?", (wid,)).fetchone()
+    if not w: raise ValueError("WG not found")
+    endpoint, local_ip, pubkey = w
+
+  ifname = f"subu_{wid}"
+  # create WG link in init ns, move to netns
+  run(["ip", "link", "add", ifname, "type", "wireguard"])
+  run(["ip", "link", "set", ifname, "netns", ns])
+  run(["ip", "-n", ns, "addr", "add", local_ip, "dev", ifname], check=False)
+  run(["ip", "-n", ns, "link", "set", "dev", ifname, "mtu", "1420"])
+  run(["ip", "-n", ns, "link", "set", "dev", ifname, "down"])  # keep engine down until `network up`
+
+  # install steering (MVP: create cgroup + attach bpf program)
+  try:
+    install_steering(subu_id, ns, ifname)
+    print(f"{subu_id}: eBPF steering installed -> {ifname}")
+  except BpfError as e:
+    print(f"{subu_id}: steering warning: {e}")
+
+  with closing(_db()) as db:
+    db.execute("UPDATE subu SET wg_id=? WHERE id=?", (wid, sid))
+    db.commit()
+  print(f"attached {wg_id} to {subu_id} in {ns} as {ifname}")
+
+def detach_wg(subu_id: str):
+  ensure_mounts()
+  sid = int(subu_id.split("_")[1])
+  with closing(_db()) as db:
+    r = db.execute("SELECT netns,wg_id FROM subu WHERE id=?", (sid,)).fetchone()
+    if not r: print("not found"); return
+    ns, wid = r
+    if wid is None:
+      print("nothing attached"); return
+  ifname = f"subu_{wid}"
+  run(["ip", "-n", ns, "link", "del", ifname], check=False)
+  try:
+    remove_steering(subu_id)
+  except BpfError as e:
+    print(f"steering remove warn: {e}")
+  with closing(_db()) as db:
+    db.execute("UPDATE subu SET wg_id=NULL WHERE id=?", (sid,))
+    db.commit()
+  print(f"detached WG_{wid} from {subu_id}")
+
+# ------------- network up/down -------------
+def network_toggle(subu_id: str, state: str):
+  sid = int(subu_id.split("_")[1])
+  with closing(_db()) as db:
+    ns, wid = db.execute("SELECT netns,wg_id FROM subu WHERE id=?", (sid,)).fetchone()
+  # always make sure lo up on 'up'
+  if state == "up":
+    run(["ip", "netns", "exec", ns, "ip", "link", "set", "lo", "up"], check=False)
+  if wid is not None:
+    ifname = f"subu_{wid}"
+    run(["ip", "-n", ns, "link", "set", "dev", ifname, state], check=False)
+  with closing(_db()) as db:
+    db.execute("UPDATE subu SET network_state=? WHERE id=?", (state, sid))
+    db.commit()
+  print(f"{subu_id}: network {state}")
+
+# ------------- options ----------------
+def option_set(subu_id: str, name: str, value: str):
+  sid = int(subu_id.split("_")[1])
+  with closing(_db()) as db:
+    db.execute("INSERT INTO options (subu_id,name,value) VALUES(?,?,?) "
+               "ON CONFLICT(subu_id,name) DO UPDATE SET value=excluded.value",
+               (sid, name, value))
+    db.commit()
+  print("ok")
+
+def option_get(subu_id: str, name: str):
+  sid = int(subu_id.split("_")[1])
+  with closing(_db()) as db:
+    row = db.execute("SELECT value FROM options WHERE subu_id=? AND name=?", (sid,name)).fetchone()
+  print(row[0] if row else "")
+
+def option_list(subu_id: str):
+  sid = int(subu_id.split("_")[1])
+  with closing(_db()) as db:
+    rows = db.execute("SELECT name,value FROM options WHERE subu_id=?", (sid,)).fetchall()
+  for n,v in rows:
+    print(f"{n}={v}")
+
+# ------------- exec -------------------
+def exec_in_subu(subu_id: str, cmd: list):
+  sid = int(subu_id.split("_")[1])
+  with closing(_db()) as db:
+    ns = db.execute("SELECT netns FROM subu WHERE id=?", (sid,)).fetchone()[0]
+  os.execvp("ip", ["ip","netns","exec", ns] + cmd)
diff --git a/developer/source/manager/subu.py b/developer/source/manager/subu.py
deleted file mode 100755 (executable)
index 92aa8e7..0000000
+++ /dev/null
@@ -1,138 +0,0 @@
-#!/usr/bin/env python3
-# -*- mode: python; coding: utf-8; python-indent-offset: 2; indent-tabs-mode: nil -*-
-
-"""
-subu.py — CLI only.
-- No-args prints USAGE.
-- `help` / `usage` / `example` / `version` are handled *before* argparse.
-- `-h` / `--help` are mapped to `help`.
-- Delegates real work to subu_core.dispatch(args).
-"""
-
-from __future__ import annotations
-import argparse
-import sys
-
-try:
-  from subu_version import VERSION
-except Exception:
-  VERSION = "0.0.0-unknown"
-
-try:
-  from subu_text import USAGE, HELP, EXAMPLE
-except Exception:
-  USAGE = "usage: subu <verb> [args]\n"
-  HELP = "help text unavailable (subu_text import failed)\n"
-  EXAMPLE = "example text unavailable (subu_text import failed)\n"
-
-# -------------------------------
-# Parser construction (verbs that do real work)
-# -------------------------------
-def _build_parser() -> argparse.ArgumentParser:
-  # add_help=False so -h/--help don't get auto-bound; we intercept them manually
-  p = argparse.ArgumentParser(
-      prog="subu",
-      description="Manage subu containers, namespaces, and WireGuard attachments.",
-      add_help=False,
-  )
-  # keep -V only; -h/--help are handled by pre-parse
-  p.add_argument("-V", "--version", action="store_true",
-                 help="Print version and exit.")
-
-  sub = p.add_subparsers(dest="verb",
-                         metavar="{init,create,info,information,WG,attach,detach,network,lo,option,exec}",
-                         required=False)
-
-  sub.add_parser("init", help="Initialize new subu database (refuses if exists).")
-  sub.add_parser("create", help="Create a subu (defaults only).")
-  sub.add_parser("info", help="Show info about a subu.")
-  sub.add_parser("information", help="Alias of 'info'.")
-  sub.add_parser("WG", help="WireGuard operations.")
-  sub.add_parser("attach", help="Attach WG to subu (netns + cgroup/eBPF).")
-  sub.add_parser("detach", help="Detach WG from subu.")
-  sub.add_parser("network", help="Bring attached ifaces up/down in the subu netns.")
-  sub.add_parser("lo", help="Bring loopback up/down in the subu netns.")
-  sub.add_parser("option", help="Persisted options (list/get/set).")
-  sub.add_parser("exec", help="Execute a command inside the subu netns: subu exec <id> -- <cmd...>")
-
-  return p
-
-def _print_topic_help(parser: argparse.ArgumentParser, topic: str) -> bool:
-  """Try to print help for a specific subparser topic. Returns True if found."""
-  for action in getattr(parser, "_subparsers", [])._actions:
-    if isinstance(action, argparse._SubParsersAction):
-      if topic in action.choices:
-        action.choices[topic].print_help()
-        return True
-      if topic == "information" and "info" in action.choices:
-        action.choices["info"].print_help()
-        return True
-  return False
-
-# -------------------------------
-# CLI entry (parse only)
-# -------------------------------
-def CLI(argv=None) -> int:
-  argv = sys.argv[1:] if argv is None else argv
-  parser = _build_parser()
-
-  # 0) No args => USAGE
-  if not argv:
-    sys.stdout.write(USAGE)
-    return 0
-
-  # 1) Pre-parse intercepts (robust vs. argparse)
-  first = argv[0]
-  if first in ("-h", "--help", "help"):
-    topic = argv[1] if len(argv) > 1 and argv[0] == "help" else None
-    if topic:
-      # Topic-aware help if possible; else fall back to full HELP
-      if not _print_topic_help(parser, topic):
-        sys.stdout.write(HELP)
-    else:
-      sys.stdout.write(HELP)
-    return 0
-
-  if first in ("usage",):
-    sys.stdout.write(USAGE)
-    return 0
-
-  if first in ("example",):
-    sys.stdout.write(EXAMPLE)
-    return 0
-
-  if first in ("version",):
-    print(VERSION)
-    return 0
-
-  # 2) Normal parse
-  try:
-    args = parser.parse_args(argv)
-  except SystemExit as e:
-    return int(e.code)
-
-  # 3) Global -V/--version
-  if getattr(args, "version", False):
-    print(VERSION)
-    return 0
-
-  # 4) Delegate to worker layer
-  try:
-    from subu_core import dispatch  # type: ignore
-  except Exception as e:
-    sys.stderr.write(f"subu: internal error: cannot import subu_core.dispatch: {e}\n")
-    return 1
-
-  try:
-    rc = dispatch(args)
-    return int(rc) if rc is not None else 0
-  except KeyboardInterrupt:
-    return 130
-  except SystemExit as e:
-    return int(e.code)
-  except Exception as e:
-    sys.stderr.write(f"subu: error: {e}\n")
-    return 1
-
-if __name__ == "__main__":
-  sys.exit(CLI())
diff --git a/developer/source/manager/subu_BPF_force_egress.c b/developer/source/manager/subu_BPF_force_egress.c
deleted file mode 100644 (file)
index 15a0085..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-// -*- mode: c; c-basic-offset: 2; indent-tabs-mode: nil; -*-
-// eBPF: force sockets inside this cgroup to use a specific ifindex
-// Hooks: cgroup/connect4 and cgroup/sendmsg4
-// Logic: read ifindex from array map[0], then setsockopt(SO_BINDTOIFINDEX)
-
-#include <linux/bpf.h>
-#include <bpf/bpf_helpers.h>
-#include <bpf/bpf_endian.h>
-
-struct {
-  __uint(type, BPF_MAP_TYPE_ARRAY);
-  __uint(max_entries, 1);
-  __type(key, __u32);
-  __type(value, __u32);   // ifindex
-  __uint(pinning, LIBBPF_PIN_BY_NAME);
-} force_ifindex_map SEC(".maps");
-
-static __always_inline int force_bind(struct bpf_sock_addr *ctx)
-{
-  __u32 k = 0;
-  __u32 *ifx = bpf_map_lookup_elem(&force_ifindex_map, &k);
-  if (!ifx || !*ifx)
-    return 1; // allow pass-through if not configured
-
-  int val = (int)*ifx;
-  // This sets sk->sk_bound_dev_if equivalently to userland SO_BINDTOIFINDEX.
-  // Ignore return (verifier- & failure-friendly).
-  (void)bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTOIFINDEX, &val, sizeof(val));
-  return 1;
-}
-
-SEC("cgroup/connect4")
-int force_dev_connect4(struct bpf_sock_addr *ctx)
-{
-  return force_bind(ctx);
-}
-
-SEC("cgroup/sendmsg4")
-int force_dev_sendmsg4(struct bpf_sock_addr *ctx)
-{
-  return force_bind(ctx);
-}
-
-char _license[] SEC("license") = "GPL";
diff --git a/developer/source/manager/subu_core.py b/developer/source/manager/subu_core.py
deleted file mode 100644 (file)
index 45ab787..0000000
+++ /dev/null
@@ -1,235 +0,0 @@
-# -*- mode: python; coding: utf-8; python-indent-offset: 2; indent-tabs-mode: nil -*-
-"""
-subu_core.py — main worker layer for Subu management
-Version 0.1.6
-"""
-
-import os, sqlite3, subprocess
-from pathlib import Path
-from contextlib import closing
-from subu_worker_bpf import install_steering, remove_steering, BpfError
-
-DB_FILE = Path("./subu.db")
-
-# ---------------------------------------------------------------------
-# SQLite helpers
-# ---------------------------------------------------------------------
-
-def db_connect():
-  if not DB_FILE.exists():
-    raise FileNotFoundError("subu.db not found; run `subu init <token>` first")
-  return sqlite3.connect(DB_FILE)
-
-def db_init():
-  if DB_FILE.exists():
-    raise FileExistsError("Database already exists")
-  with closing(sqlite3.connect(DB_FILE)) as db:
-    c = db.cursor()
-    c.executescript("""
-      CREATE TABLE subu (
-        id INTEGER PRIMARY KEY AUTOINCREMENT,
-        owner TEXT,
-        name TEXT,
-        netns TEXT,
-        lo_state TEXT DEFAULT 'down',
-        wg_id INTEGER,
-        network_state TEXT DEFAULT 'down'
-      );
-      CREATE TABLE wg (
-        id INTEGER PRIMARY KEY AUTOINCREMENT,
-        endpoint TEXT,
-        local_ip TEXT,
-        allowed_ips TEXT,
-        pubkey TEXT,
-        state TEXT DEFAULT 'down'
-      );
-      CREATE TABLE options (
-        subu_id INTEGER,
-        name TEXT,
-        value TEXT,
-        PRIMARY KEY (subu_id, name)
-      );
-    """)
-    db.commit()
-  print("✅ subu.db created")
-
-# ---------------------------------------------------------------------
-# System helpers
-# ---------------------------------------------------------------------
-
-def run(cmd, check=True):
-  r = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
-  if check and r.returncode != 0:
-    raise RuntimeError(f"cmd failed: {' '.join(cmd)}\n{r.stderr}")
-  return r.stdout.strip()
-
-def create_netns(nsname: str):
-  run(["ip", "netns", "add", nsname])
-  run(["ip", "-n", nsname, "link", "set", "lo", "down"])
-  return nsname
-
-def delete_netns(nsname: str):
-  run(["ip", "netns", "delete", nsname], check=False)
-
-def ifindex_in_netns(nsname: str, ifname: str) -> int:
-  out = run(["ip", "-n", nsname, "-o", "link", "show", ifname])
-  return int(out.split(":", 1)[0])
-
-# ---------------------------------------------------------------------
-# Subu operations
-# ---------------------------------------------------------------------
-
-def create_subu(owner: str, name: str) -> str:
-  with closing(db_connect()) as db:
-    c = db.cursor()
-    c.execute("INSERT INTO subu (owner, name, netns) VALUES (?, ?, ?)",
-              (owner, name, f"ns-{owner}-{name}"))
-    subu_id = c.lastrowid
-    db.commit()
-  nsname = f"ns-subu_{subu_id}"
-  create_netns(nsname)
-  print(f"Created subu_{subu_id} ({owner}:{name}) with netns {nsname}")
-  return f"subu_{subu_id}"
-
-def list_subu():
-  with closing(db_connect()) as db:
-    for row in db.execute("SELECT id, owner, name, netns, lo_state, wg_id, network_state FROM subu"):
-      print(row)
-
-def info_subu(subu_id: str):
-  sid = int(subu_id.split("_")[1])
-  with closing(db_connect()) as db:
-    for row in db.execute("SELECT * FROM subu WHERE id=?", (sid,)):
-      print(row)
-
-def lo_toggle(subu_id: str, state: str):
-  sid = int(subu_id.split("_")[1])
-  with closing(db_connect()) as db:
-    row = db.execute("SELECT netns FROM subu WHERE id=?", (sid,)).fetchone()
-    if not row: raise ValueError("subu not found")
-    ns = row[0]
-    run(["ip", "netns", "exec", ns, "ip", "link", "set", "lo", state])
-    db.execute("UPDATE subu SET lo_state=? WHERE id=?", (state, sid))
-    db.commit()
-  print(f"loopback {state} in {subu_id}")
-
-# ---------------------------------------------------------------------
-# WireGuard operations
-# ---------------------------------------------------------------------
-
-def wg_global(basecidr: str):
-  Path("./WG_GLOBAL").write_text(basecidr.strip() + "\n")
-  print(f"Base CIDR set to {basecidr}")
-
-def wg_create(endpoint: str) -> str:
-  base = Path("./WG_GLOBAL").read_text().strip() if Path("./WG_GLOBAL").exists() else None
-  if not base:
-    raise RuntimeError("No WG global base; set with `subu WG global`")
-  with closing(db_connect()) as db:
-    c = db.cursor()
-    # trivial allocator: next /32 by count
-    idx = c.execute("SELECT COUNT(*) FROM wg").fetchone()[0]
-    octets = base.split(".")
-    octets[3] = str(2 + idx)
-    local_ip = ".".join(octets) + "/32"
-    c.execute("INSERT INTO wg (endpoint, local_ip, allowed_ips) VALUES (?, ?, ?)",
-              (endpoint, local_ip, "0.0.0.0/0"))
-    wid = c.lastrowid
-    db.commit()
-  print(f"Created WG_{wid} ({endpoint}) local_ip={local_ip}")
-  return f"WG_{wid}"
-
-def wg_set_pubkey(wg_id: str, key: str):
-  wid = int(wg_id.split("_")[1])
-  with closing(db_connect()) as db:
-    db.execute("UPDATE wg SET pubkey=? WHERE id=?", (key, wid))
-    db.commit()
-  print(f"Public key stored for {wg_id}")
-
-def wg_info(wg_id: str):
-  wid = int(wg_id.split("_")[1])
-  with closing(db_connect()) as db:
-    row = db.execute("SELECT * FROM wg WHERE id=?", (wid,)).fetchone()
-    if not row: print("WG not found")
-    else: print(row)
-
-# ---------------------------------------------------------------------
-# Attach / Detach with eBPF steering
-# ---------------------------------------------------------------------
-
-def attach_wg(subu_id: str, wg_id: str):
-  sid = int(subu_id.split("_")[1])
-  wid = int(wg_id.split("_")[1])
-  wg_ifname = f"subu_{wid}"
-  netns = f"ns-{subu_id}"
-
-  # Create WG device inside namespace
-  run(["ip", "link", "add", wg_ifname, "type", "wireguard"])
-  run(["ip", "link", "set", wg_ifname, "netns", netns])
-  # Configure MTU + accept_local
-  run(["ip", "-n", netns, "link", "set", wg_ifname, "mtu", "1420"])
-  run(["ip", "-n", netns, "link", "set", "dev", wg_ifname, "up"])
-  print(f"Attached {wg_id} as {wg_ifname} inside {netns}")
-
-  # Install steering
-  try:
-    install_steering(subu_id, netns, wg_ifname)
-    print(f"Installed eBPF steering for {subu_id} via {wg_ifname}")
-  except BpfError as e:
-    print(f"warning: steering failed: {e}")
-
-  # Update DB linkage
-  with closing(db_connect()) as db:
-    db.execute("UPDATE subu SET wg_id=? WHERE id=?", (wid, sid))
-    db.commit()
-
-def detach_wg(subu_id: str):
-  sid = int(subu_id.split("_")[1])
-  with closing(db_connect()) as db:
-    row = db.execute("SELECT wg_id, netns FROM subu WHERE id=?", (sid,)).fetchone()
-    if not row or row[0] is None:
-      print("nothing attached")
-      return
-    wid, ns = row
-    wg_ifname = f"subu_{wid}"
-    run(["ip", "-n", ns, "link", "del", wg_ifname], check=False)
-    db.execute("UPDATE subu SET wg_id=NULL WHERE id=?", (sid,))
-    db.commit()
-  try:
-    remove_steering(subu_id)
-    print(f"Removed steering for {subu_id}")
-  except BpfError as e:
-    print(f"warning: remove steering failed: {e}")
-
-# ---------------------------------------------------------------------
-# Network up/down aggregate
-# ---------------------------------------------------------------------
-
-def network_toggle(subu_id: str, state: str):
-  sid = int(subu_id.split("_")[1])
-  with closing(db_connect()) as db:
-    row = db.execute("SELECT netns, wg_id FROM subu WHERE id=?", (sid,)).fetchone()
-    if not row: raise ValueError("subu not found")
-    ns, wid = row
-  # bring lo up first if needed
-  if state == "up":
-    run(["ip", "netns", "exec", ns, "ip", "link", "set", "lo", "up"], check=False)
-  # bring attached iface
-  if wid:
-    ifname = f"subu_{wid}"
-    run(["ip", "-n", ns, "link", "set", "dev", ifname, state], check=False)
-  with closing(db_connect()) as db:
-    db.execute("UPDATE subu SET network_state=? WHERE id=?", (state, sid))
-    db.commit()
-  print(f"{subu_id}: network {state}")
-
-# ---------------------------------------------------------------------
-# Exec inside namespace
-# ---------------------------------------------------------------------
-
-def exec_in_subu(subu_id: str, cmd: list):
-  sid = int(subu_id.split("_")[1])
-  with closing(db_connect()) as db:
-    ns = db.execute("SELECT netns FROM subu WHERE id=?", (sid,)).fetchone()[0]
-  full = ["ip", "netns", "exec", ns] + cmd
-  os.execvp(full[0], full)
diff --git a/developer/source/manager/temp.sh b/developer/source/manager/temp.sh
new file mode 100644 (file)
index 0000000..36855b6
--- /dev/null
@@ -0,0 +1,40 @@
+# from: /home/Thomas/subu_data/developer/project/active/subu/developer/source/manager
+
+set -euo pipefail
+
+echo "== 1) Backup legacy-prefixed modules =="
+mkdir -p _old_prefixed
+for f in subu_*.py; do
+  [ -f "$f" ] && mv -v "$f" _old_prefixed/
+done
+[ -f subu_worker_bpf.py ] && mv -v subu_worker_bpf.py _old_prefixed/ || true
+
+echo "== 2) Ensure only the new module names remain =="
+# Keep these (already present in your tar):
+#   CLI.py  core.py  text.py  worker_bpf.py  bpf_force_egress.c
+ls -1
+
+echo "== 3) Make CLI runnable as 'subu' =="
+# Make sure CLI has a shebang; add if missing
+if ! head -n1 CLI.py | grep -q '^#!/usr/bin/env python3'; then
+  (printf '%s\n' '#!/usr/bin/env python3' ; cat CLI.py) > .CLI.tmp && mv .CLI.tmp CLI.py
+fi
+chmod +x CLI.py
+ln -sf CLI.py subu
+chmod +x subu
+
+echo "== 4) Quick import sanity =="
+# Fail if any of the remaining files still import the old module names
+bad=$(grep -R --line-number -E 'import +subu_|from +subu_' -- *.py || true)
+if [ -n "$bad" ]; then
+  echo "Found old-style imports; please fix:" >&2
+  echo "$bad" >&2
+  exit 1
+fi
+
+echo "== 5) Show version and help =="
+./subu version || true
+./subu help    || true
+./subu         || true  # should print usage by default
+
+echo "== Done. If this looks good, you can delete _old_prefixed when ready. =="
diff --git a/developer/source/manager/text.py b/developer/source/manager/text.py
new file mode 100644 (file)
index 0000000..84f6762
--- /dev/null
@@ -0,0 +1,109 @@
+# -*- mode: python; coding: utf-8; python-indent-offset: 2; indent-tabs-mode: nil -*-
+VERSION = "0.2.0"
+
+USAGE = """\
+subu — Subu manager (v0.2.0)
+
+Usage:
+  subu                   # usage
+  subu help              # detailed help
+  subu example           # example workflow
+  subu version           # print version
+
+  subu init <TOKEN>
+  subu create <owner> <name>
+  subu list
+  subu info <Subu_ID> | subu information <Subu_ID>
+
+  subu lo up|down <Subu_ID>
+
+  subu WG global <BaseCIDR>
+  subu WG create <host:port>
+  subu WG server_provided_public_key <WG_ID> <Base64Key>
+  subu WG info|information <WG_ID>
+  subu WG up <WG_ID>
+  subu WG down <WG_ID>
+
+  subu attach WG <Subu_ID> <WG_ID>
+  subu detach WG <Subu_ID>
+
+  subu network up|down <Subu_ID>
+
+  subu option set <Subu_ID> <name> <value>
+  subu option get <Subu_ID> <name>
+  subu option list <Subu_ID>
+
+  subu exec <Subu_ID> -- <cmd> ...
+"""
+
+HELP = """\
+Subu manager (v0.2.0)
+
+1) Init
+  subu init <TOKEN>
+    Creates ./subu.db. Refuses to run if db exists.
+
+2) Subu
+  subu create <owner> <name>
+  subu list
+  subu info <Subu_ID>
+
+3) Loopback
+  subu lo up|down <Subu_ID>
+
+4) WireGuard objects (independent of subu)
+  subu WG global <BaseCIDR>                 # e.g., 192.168.112.0/24
+  subu WG create <host:port>                # allocates next /32
+  subu WG server_provided_public_key <WG_ID> <Base64Key>
+  subu WG info <WG_ID>
+  subu WG up <WG_ID> / subu WG down <WG_ID> # admin toggle after attached
+
+5) Attach/detach + eBPF steering
+  subu attach WG <Subu_ID> <WG_ID>
+    - Creates WG dev as subu_<M> inside ns-subu_<N>, assigns /32, MTU 1420
+    - Installs per-subu cgroup + loads eBPF scaffold (UID check, metadata map)
+    - Keeps device admin-down until `subu network up`
+  subu detach WG <Subu_ID>
+    - Deletes device, removes cgroup + BPF
+
+6) Network aggregate
+  subu network up|down <Subu_ID>
+    - Ensures lo up on 'up', toggles attached WG ifaces
+
+7) Options
+  subu option set|get|list ...
+
+8) Exec
+  subu exec <Subu_ID> -- <cmd> ...
+"""
+
+EXAMPLE = """\
+# 0) Init
+subu init dzkq7b
+
+# 1) Create Subu
+subu create Thomas US
+# -> subu_1
+
+# 2) WG pool once
+subu WG global 192.168.112.0/24
+
+# 3) Create WG object with endpoint
+subu WG create ReasoningTechnology.com:51820
+# -> WG_1
+
+# 4) Pubkey (placeholder)
+subu WG server_provided_public_key WG_1 ABCDEFG...xyz=
+
+# 5) Attach device and install cgroup+BPF steering
+subu attach WG subu_1 WG_1
+
+# 6) Bring network up (lo + WG)
+subu network up subu_1
+
+# 7) Test inside ns
+subu exec subu_1 -- curl -4v https://ifconfig.me
+"""
+
+def VERSION_string():
+  return VERSION
diff --git a/developer/source/manager/worker_bpf.py b/developer/source/manager/worker_bpf.py
new file mode 100644 (file)
index 0000000..96aef14
--- /dev/null
@@ -0,0 +1,78 @@
+# -*- mode: python; coding: utf-8; python-indent-offset: 2; indent-tabs-mode: nil -*-
+"""
+worker_bpf.py — create per-subu cgroups and load eBPF (MVP)
+Version: 0.2.0
+"""
+import os, subprocess, json
+from pathlib import Path
+
+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)}\n{r.stderr}")
+  return r.stdout.strip()
+
+def ensure_mounts():
+  # ensure bpf and cgroup v2 are mounted
+  try:
+    Path("/sys/fs/bpf").mkdir(parents=True, exist_ok=True)
+    run(["mount","-t","bpf","bpf","/sys/fs/bpf"], check=False)
+  except Exception:
+    pass
+  try:
+    Path("/sys/fs/cgroup").mkdir(parents=True, exist_ok=True)
+    run(["mount","-t","cgroup2","none","/sys/fs/cgroup"], check=False)
+  except Exception:
+    pass
+
+def cgroup_path(subu_id: str) -> str:
+  return f"/sys/fs/cgroup/{subu_id}"
+
+def install_steering(subu_id: str, netns: str, ifname: str):
+  ensure_mounts()
+  cg = Path(cgroup_path(subu_id))
+  cg.mkdir(parents=True, exist_ok=True)
+
+  # compile BPF
+  obj = Path("./bpf_force_egress.o")
+  src = Path("./bpf_force_egress.c")
+  if not src.exists():
+    raise BpfError("bpf_force_egress.c missing next to manager")
+
+  # Build object (requires clang/llc/bpftool)
+  run(["clang","-O2","-g","-target","bpf","-c",str(src),"-o",str(obj)])
+
+  # Load program into bpffs; attach to cgroup/inet4_connect + inet4_post_bind (MVP)
+  pinned = f"/sys/fs/bpf/{subu_id}_egress"
+  run(["bpftool","prog","loadall",str(obj),pinned], check=True)
+
+  # Attach to hooks (MVP validation hooks)
+  # NOTE: these are safe no-ops for now; they validate UID and stash ifindex map.
+  for hook in ("cgroup/connect4","cgroup/post_bind4"):
+    run(["bpftool","cgroup","attach",cgroup_path(subu_id),"attach",hook,"pinned",f"{pinned}/prog_0"], check=False)
+
+  # Write metadata for ifname (saved for future prog versions)
+  meta = {"ifname": ifname}
+  Path(f"/sys/fs/bpf/{subu_id}_meta.json").write_text(json.dumps(meta))
+
+def remove_steering(subu_id: str):
+  cg = cgroup_path(subu_id)
+  # Detach whatever is attached
+  for hook in ("cgroup/connect4","cgroup/post_bind4"):
+    subprocess.run(["bpftool","cgroup","detach",cg,"detach",hook], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
+  # Remove pinned prog dir
+  pinned = Path(f"/sys/fs/bpf/{subu_id}_egress")
+  if pinned.exists():
+    subprocess.run(["bpftool","prog","detach",str(pinned)], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
+    try:
+      for p in pinned.glob("*"): p.unlink()
+      pinned.rmdir()
+    except Exception:
+      pass
+  # Remove cgroup dir
+  try:
+    Path(cg).rmdir()
+  except Exception:
+    pass