"""Small CLI for Mission CSFD operations. Usage: python -m missionledger.cli generate --plandelta plan.json python -m missionledger.cli inspect --plandelta plan.json python -m missionledger.cli sign --plandelta plan.json --keyfile key.bin python -m missionledger.cli verify --plandelta plan.json --signature SIGHEX --vkfile vk.bin This is a lightweight tool meant for demos and tests. For production use, integrate with a proper key management system and secure proof stores. """ from __future__ import annotations import argparse import json import sys from typing import Optional from .dsl import PlanDelta from .csfd import generate_csfd, sign_csfd, verify_csfd_signature from .csfd import csfd_to_cbor, csfd_from_cbor def load_plandelta(path: str) -> PlanDelta: with open(path, "rb") as f: data = json.load(f) # allow plain dicts matching PlanDelta fields return PlanDelta(delta=data.get("delta", {}), version=int(data.get("version", 0)), metadata=data.get("metadata", {})) def cmd_generate(args: argparse.Namespace) -> int: pd = load_plandelta(args.plandelta) csfd = generate_csfd(pd, signer=args.signer or "", n_frames=args.nframes) print(json.dumps({"frames": [f.__dict__ for f in csfd.frames], "signer": csfd.signer, "digest_hex": csfd.digest_hex}, sort_keys=True, separators=(",", ":"))) return 0 def cmd_inspect(args: argparse.Namespace) -> int: # inspect is same as generate but pretty-prints pd = load_plandelta(args.plandelta) csfd = generate_csfd(pd, signer=args.signer or "", n_frames=args.nframes) print(json.dumps({"frames": [f.__dict__ for f in csfd.frames], "signer": csfd.signer, "digest_hex": csfd.digest_hex}, indent=2, sort_keys=True)) return 0 def cmd_sign(args: argparse.Namespace) -> int: pd = load_plandelta(args.plandelta) csfd = generate_csfd(pd, signer=args.signer or "", n_frames=args.nframes) # read key bytes with open(args.keyfile, "rb") as f: keyb = f.read() sig = sign_csfd(csfd, keyb) out = {"signature": sig, "digest_hex": csfd.digest_hex} print(json.dumps(out, sort_keys=True, separators=(",", ":"))) return 0 def cmd_verify(args: argparse.Namespace) -> int: pd = load_plandelta(args.plandelta) csfd = generate_csfd(pd, signer=args.signer or "", n_frames=args.nframes) with open(args.vkfile, "rb") as f: vk = f.read() ok = verify_csfd_signature(csfd, args.signature, vk) print(json.dumps({"ok": bool(ok)})) return 0 def cmd_export_cbor(args: argparse.Namespace) -> int: pd = load_plandelta(args.plandelta) csfd = generate_csfd(pd, signer=args.signer or "", n_frames=args.nframes) b = csfd_to_cbor(csfd) # write bytes to file with open(args.outfile, "wb") as f: f.write(b) print(json.dumps({"outfile": args.outfile, "digest_hex": csfd.digest_hex})) return 0 def cmd_import_cbor(args: argparse.Namespace) -> int: # read bytes and print inspected CSFD with open(args.infile, "rb") as f: b = f.read() csfd = csfd_from_cbor(b) print(json.dumps({"frames": [f.__dict__ for f in csfd.frames], "signer": csfd.signer, "digest_hex": csfd.digest_hex}, indent=2, sort_keys=True)) return 0 def main(argv: Optional[list[str]] = None) -> int: p = argparse.ArgumentParser(prog="mission-csfd", description="Mission CSFD helper CLI (demo)") sub = p.add_subparsers(dest="cmd") gen = sub.add_parser("generate") gen.add_argument("--plandelta", required=True) gen.add_argument("--signer", required=False) gen.add_argument("--nframes", type=int, default=3) gen.set_defaults(func=cmd_generate) ins = sub.add_parser("inspect") ins.add_argument("--plandelta", required=True) ins.add_argument("--signer", required=False) ins.add_argument("--nframes", type=int, default=3) ins.set_defaults(func=cmd_inspect) signp = sub.add_parser("sign") signp.add_argument("--plandelta", required=True) signp.add_argument("--keyfile", required=True) signp.add_argument("--signer", required=False) signp.add_argument("--nframes", type=int, default=1) signp.set_defaults(func=cmd_sign) ver = sub.add_parser("verify") ver.add_argument("--plandelta", required=True) ver.add_argument("--signature", required=True) ver.add_argument("--vkfile", required=True) ver.add_argument("--signer", required=False) ver.add_argument("--nframes", type=int, default=1) ver.set_defaults(func=cmd_verify) exp = sub.add_parser("export-cbor") exp.add_argument("--plandelta", required=True) exp.add_argument("--outfile", required=True) exp.add_argument("--signer", required=False) exp.add_argument("--nframes", type=int, default=3) exp.set_defaults(func=cmd_export_cbor) imp = sub.add_parser("import-cbor") imp.add_argument("--infile", required=True) imp.set_defaults(func=cmd_import_cbor) args = p.parse_args(argv) if not hasattr(args, "func"): p.print_help() return 2 return args.func(args) if __name__ == "__main__": raise SystemExit(main())