{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": "# fak quickstart - fresh value demos\n\nBuild the current `fak` once, then run the case that matches what you are trying to prove.\nThe notebook defaults to `FAK_BRANCH=main`, refreshes an existing clone unless\n`FAK_REFRESH=0`, and prints the exact commit and binary version it is about to run.\n\n| Case | Use this when... | What it proves | Needs |\n|---|---|---|---|\n| **A. Policy floor** | you need a reviewable allow/deny boundary | `preflight --json` explains the winning rung; `attest` proves the manifest | CPU |\n| **B. App/API drop-in** | you have a Python app, MCP adapter, or tool runner | `/v1/fak/adjudicate` gives a verdict before your code executes a tool | CPU |\n| **C. Value measurement** | you want a number before wiring a model | offline benchmark catalog, vDSO transport A/B, and turn-tax report | CPU |\n| **D. Real model gateway** | you want `fak` in front of a chat model | OpenAI-compatible client -> `fak serve` -> Ollama | GPU recommended |\n\nNo API key is needed for cases A-C. Case D uses a free Colab/Kaggle GPU and a small Ollama\nmodel by default.\n\n> _Generated by `tools/gen_notebooks.py` \u2014 edit the generator, not this `.ipynb`. CI guards drift + stale references via `--check`._"
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": "# --- setup: work dir, repo coords, GPU ---\nimport os, shutil, subprocess, sys, time, json, urllib.request\n\nWORK = os.environ.get(\"FAK_WORK\") or (\"/content\" if os.path.isdir(\"/content\")\n        else \"/kaggle/working\" if os.path.isdir(\"/kaggle/working\") else \"/tmp/fak-demo\")\nos.makedirs(WORK, exist_ok=True); os.chdir(WORK)\n\nREPO   = os.environ.get(\"FAK_REPO\", \"anthony-chaudhary/fak\")\nBRANCH = os.environ.get(\"FAK_BRANCH\", \"main\")  # pin a release tag here, e.g. v0.30.0\n\nHAS_GPU = shutil.which(\"nvidia-smi\") is not None and \\\n          subprocess.run([\"nvidia-smi\"], capture_output=True).returncode == 0\nprint(\"work dir :\", WORK)\nprint(f\"repo     : {REPO}@{BRANCH}\")\nprint(\"GPU      :\", \"yes \u2014 Tier 1 ready\" if HAS_GPU else \"no \u2014 Tier 0 only (Runtime -> T4 for Tier 1)\")"
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": "# --- get a fresh `fak` binary plus the examples/testdata the demos use.\n#     The Go module is the repository ROOT (go.mod), so the clone dir *is* the build dir.\n#     By default an existing clone is refreshed to FAK_BRANCH. Set FAK_REFRESH=0 to reuse it.\nimport urllib.request, tarfile, platform, re\n\ndef sh(cmd, timeout=900):\n    print(\"$\", cmd)\n    subprocess.run(cmd, shell=True, check=True, timeout=timeout)\n\ndef go_ok():\n    try:\n        out = subprocess.run([\"go\",\"version\"], capture_output=True, text=True, check=True).stdout\n        m = re.search(r\"go(\\d+)\\.(\\d+)\", out)\n        return bool(m) and (int(m.group(1)), int(m.group(2))) >= (1, 26)\n    except Exception:\n        return False\n\ndef latest_go_archive():\n    arch = platform.machine().lower()\n    goarch = {\"x86_64\":\"amd64\", \"amd64\":\"amd64\", \"aarch64\":\"arm64\", \"arm64\":\"arm64\"}.get(arch)\n    if sys.platform.startswith(\"linux\") and goarch:\n        rels = json.load(urllib.request.urlopen(\"https://go.dev/dl/?mode=json\", timeout=30))\n        for rel in rels:\n            m = re.match(r\"go(\\d+)\\.(\\d+)\", rel.get(\"version\", \"\"))\n            if not m or (int(m.group(1)), int(m.group(2))) < (1, 26):\n                continue\n            for f in rel.get(\"files\", []):\n                if f.get(\"kind\") == \"archive\" and f.get(\"os\") == \"linux\" and f.get(\"arch\") == goarch:\n                    return rel[\"version\"], f[\"filename\"]\n    return \"go1.26.3\", \"go1.26.3.linux-amd64.tar.gz\"\n\nif not go_ok():\n    GOV, GO = latest_go_archive()\n    print(\"installing\", GOV, \"...\")\n    urllib.request.urlretrieve(\"https://go.dev/dl/\" + GO, \"/tmp/\" + GO)\n    shutil.rmtree(\"/usr/local/go\", ignore_errors=True)\n    with tarfile.open(\"/tmp/\" + GO) as t: t.extractall(\"/usr/local\")\nos.environ[\"PATH\"] = \"/usr/local/go/bin:\" + os.environ[\"PATH\"]\nprint(subprocess.run([\"go\",\"version\"], capture_output=True, text=True).stdout.strip())\n\n# clone the public repo (anonymous; no token needed), then refresh to the requested ref\nFAK_DIR = os.path.join(WORK, \"fak\")\nif not os.path.isdir(os.path.join(FAK_DIR, \".git\")):\n    print(f\"$ git clone --depth 1 -b {BRANCH} https://github.com/{REPO}.git  ->  {FAK_DIR}\")\n    subprocess.run([\"git\",\"clone\",\"--depth\",\"1\",\"-b\",BRANCH,\n                    f\"https://github.com/{REPO}.git\", FAK_DIR], check=True)\nelse:\n    print(\"clone exists:\", FAK_DIR)\n    if os.environ.get(\"FAK_REFRESH\", \"1\") != \"0\":\n        sh(f'git -C \"{FAK_DIR}\" fetch --depth 1 origin \"{BRANCH}\"')\n        sh(f'git -C \"{FAK_DIR}\" checkout -q --detach FETCH_HEAD')\n    else:\n        print(\"FAK_REFRESH=0, reusing the existing checkout without fetch\")\n\nstamp = subprocess.run([\"git\",\"-C\",FAK_DIR,\"log\",\"-1\",\"--format=%h %cI %s\"],\n                       capture_output=True, text=True, check=True).stdout.strip()\nprint(\"source  :\", stamp)\n\n# the module is the repo root, so build right here\nFAK_EXE = \"fak.exe\" if os.name == \"nt\" else \"fak\"\nFAK = os.path.join(FAK_DIR, FAK_EXE)\nsh(f'cd \"{FAK_DIR}\" && go build -o \"{FAK_EXE}\" ./cmd/fak')\nprint(\"\\nfak ->\", FAK)\nprint(subprocess.run([FAK,\"version\"], capture_output=True, text=True).stdout.strip() or \"built ok\")"
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": "# --- small helpers used by the cases below ---\nPOL = \"examples/customer-support-readonly-policy.json\"\n\ndef cmd(argv, cwd=FAK_DIR, check=True, timeout=180):\n    p = subprocess.run(argv, cwd=cwd, capture_output=True, text=True, timeout=timeout)\n    if check and p.returncode != 0:\n        raise RuntimeError(\"command failed: \" + \" \".join(argv) + \"\\n\" + p.stdout + p.stderr)\n    return p\n\ndef fak(*args, check=True, timeout=180):\n    return cmd([FAK, *args], check=check, timeout=timeout)\n\ndef fak_json(*args, timeout=180):\n    p = fak(*args, timeout=timeout)\n    return json.loads(p.stdout)\n\ndef clip(s, n=2400):\n    s = s.strip()\n    return s if len(s) <= n else s[:n] + \"\\n... clipped ...\"\n\ndef post_json(base, path, payload, timeout=30):\n    req = urllib.request.Request(base + path, data=json.dumps(payload).encode(),\n                                 headers={\"Content-Type\": \"application/json\"})\n    with urllib.request.urlopen(req, timeout=timeout) as r:\n        return json.load(r)\n\ndef stop_process(name):\n    p = globals().get(name)\n    if p and p.poll() is None:\n        p.terminate()\n        try:\n            p.wait(timeout=5)\n        except subprocess.TimeoutExpired:\n            p.kill()\n\nprint(\"source :\", cmd([\"git\",\"log\",\"-1\",\"--format=%h %cI %s\"]).stdout.strip())\nprint(\"binary :\")\nprint(fak(\"version\").stdout.strip())"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": "## Expected state for a CPU run\n\nRun-all on a CPU-only runtime should finish Cases A-C and print these success signals:\n\n- Case A: `refund_payment` is `DENY/POLICY_BLOCK`, `search_kb` is `ALLOW`, and the policy attestation passes with zero failed probes.\n- Case B: local `fak serve` reports healthy, and `/v1/fak/adjudicate` denies `refund_payment` before your app executes it.\n- Case C: `benchmarks list --offline` prints the zero-asset benchmark catalog, `bench` reports `PRIMARY GATE : pass`, and `turntax` reports 9 turns saved on the fixture.\n- Case D: skipped on CPU-only runtimes; on a GPU runtime it should serve `qwen2.5:1.5b` through the OpenAI SDK.\n\nThe cells below include assertions for the stable CPU expectations, so a stale policy,\nmissing verb, or broken HTTP adjudication path fails where it happens."
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": "## Case A - policy floor you can ship\n\nThis is the no-model proof. `preflight` runs the same adjudication fold a served call uses,\nand `attest` turns the policy file into a re-checkable proof obligation."
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": "# A1. Three structural calls: explicit deny, prefix allow, fail-closed unknown.\ncases = [\n    (\"explicit deny\", \"refund_payment\", {}, \"DENY\", \"POLICY_BLOCK\"),\n    (\"prefix allow\", \"search_kb\", {\"query\": \"refund policy\"}, \"ALLOW\", \"\"),\n    (\"fail-closed unknown\", \"__not_in_policy__\", {}, \"DENY\", \"DEFAULT_DENY\"),\n]\nfor label, tool, args, want_kind, want_reason in cases:\n    d0 = fak_json(\"preflight\", \"--policy\", POL, \"--tool\", tool, \"--args\", json.dumps(args),\n                  \"--json\")\n    got = (d0[\"verdict\"], d0.get(\"reason\", \"\"))\n    assert d0[\"verdict\"] == want_kind, (label, got, want_kind)\n    if want_reason:\n        assert d0.get(\"reason\") == want_reason, (label, got, want_reason)\n    print(f\"{label:<20} {tool:<22} -> {d0['verdict']} {d0.get('reason','')}\")\n\n# A2. Explain the exact winning rung without logging raw args.\nd = fak_json(\"preflight\", \"--policy\", POL, \"--tool\", \"refund_payment\", \"--args\", \"{}\",\n             \"--json\")\nprint(\"\\nwinning verdict:\", d[\"verdict\"], d.get(\"reason\", \"\"), \"by\", d.get(\"by\", \"\"))\nprint(\"explanation    :\", d[\"explanation\"])\nprint(\"\\nrung trace:\")\nfor r in d[\"rungs\"]:\n    mark = \"WIN\" if r.get(\"winner\") else \"   \"\n    print(f\"{mark} [{r['index']}] {r['rung']:<26} {r.get('kind','ELIDED'):<9} {r.get('reason','')}\")"
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": "# A3. Prove the whole manifest, not just one cherry-picked call.\natt = fak_json(\"attest\", \"--policy\", POL, \"--quiet\")\nassert att[\"summary\"][\"pass\"] is True, att[\"summary\"]\nassert att[\"summary\"][\"failed\"] == 0, att[\"summary\"]\nprint(\"attestation schema:\", att[\"schema\"])\nprint(\"policy sha256     :\", att[\"policy\"][\"sha256\"][:16] + \"...\")\nprint(\"probes            :\", att[\"summary\"][\"probes\"])\nprint(\"passed / failed   :\", att[\"summary\"][\"passed\"], \"/\", att[\"summary\"][\"failed\"])\nprint(\"floor proven      :\", att[\"summary\"][\"pass\"])"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": "## Case B - app/API drop-in\n\nUse this path when your own application executes tools. The application asks `fak` for a\nverdict first, then executes only the calls that survive. No chat model or GPU is involved."
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": "# Start a local fak HTTP gateway for adjudication-only calls.\nstop_process(\"fak_api_proc\")\nAPI_ADDR = os.environ.get(\"FAK_API_ADDR\", \"127.0.0.1:8765\")\nAPI = \"http://\" + API_ADDR\nfak_api_proc = subprocess.Popen(\n    [FAK, \"serve\", \"--addr\", API_ADDR, \"--policy\", POL, \"--model\", \"quickstart\"],\n    cwd=FAK_DIR, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)\n\nfor _ in range(60):\n    try:\n        with urllib.request.urlopen(API + \"/healthz\", timeout=2) as r:\n            print(\"gateway:\", r.read().decode().strip())\n            break\n    except Exception:\n        time.sleep(1)\nelse:\n    raise RuntimeError(\"fak serve did not become healthy on \" + API)"
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": "# The same endpoint accepts JSON objects and OpenAI-style JSON-string arguments.\napi_cases = [\n    (\"read-only app action\", {\"tool\": \"search_kb\", \"arguments\": {\"query\": \"refund policy\"},\n                              \"read_only\": True}),\n    (\"blocked business action\", {\"tool\": \"refund_payment\", \"arguments\": {\"amount\": 100}}),\n    (\"OpenAI function args\", {\"tool\": \"refund_payment\",\n                              \"arguments\": json.dumps({\"amount\": 100})}),\n]\nfor label, payload in api_cases:\n    r = post_json(API, \"/v1/fak/adjudicate\", payload)\n    v = r[\"verdict\"]\n    if payload[\"tool\"] == \"refund_payment\":\n        assert v[\"kind\"] == \"DENY\" and v.get(\"reason\") == \"POLICY_BLOCK\", v\n    if payload[\"tool\"] == \"search_kb\":\n        assert v[\"kind\"] == \"ALLOW\", v\n    print(f\"{label:<24} -> {v['kind']:<5} {v.get('reason','')} {v.get('disposition','')}\")\n\nprint(\"\\nThis was adjudicate-only: your application still owns execution of allowed tools.\")"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": "## Case C - value measurement without a model\n\nBefore you spend GPU time, ask what can be measured offline. The catalog names the benchmark\nsurfaces, then two small runs show the transport/value axes on committed fixtures."
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": "# Discover the zero-asset benchmark surfaces.\nprint(clip(fak(\"benchmarks\", \"list\", \"--offline\").stdout, 3600))"
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": "# Run the short transport benchmark and a turn-tax report on frozen fixtures.\nprint(\"== transport A/B ==\")\nbench_out = fak(\"bench\", \"--suite\", \"tau2-smoke\", \"--baseline-n\", \"5\",\n                \"--out\", \"quickstart-bench.json\", timeout=240).stdout\nassert \"PRIMARY GATE\" in bench_out and \"pass\" in bench_out, bench_out\nprint(clip(bench_out, 2200))\n\nprint(\"\\n== turn-tax A/B ==\")\nturntax_out = fak(\"turntax\", \"--suite\", \"turntax-airline\",\n                  \"--out\", \"quickstart-turntax.json\", timeout=240).stdout\nassert \"turns saved\" in turntax_out and \": 9\" in turntax_out, turntax_out\nprint(clip(turntax_out, 2600))"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": "## Case D - real model gateway\n\nIf the runtime has a GPU, this starts Ollama with a small model and puts `fak serve` in front\nof it. The client is the standard OpenAI Python SDK pointed at `http://127.0.0.1:8080/v1`."
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": "# D1. Start Ollama and pull a small model that fits a free T4.\nMODEL = os.environ.get(\"FAK_MODEL\", \"qwen2.5:1.5b\")\n\nif not HAS_GPU:\n    print(\"no GPU - skipping Case D. Switch Runtime -> Change runtime type -> T4 and run again.\")\nelse:\n    if shutil.which(\"ollama\") is None:\n        subprocess.run(\"curl -fsSL https://ollama.com/install.sh | sh\", shell=True, check=True)\n    if not globals().get(\"ollama_proc\") or ollama_proc.poll() is not None:\n        ollama_proc = subprocess.Popen([\"ollama\", \"serve\"],\n                                       stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)\n    for _ in range(60):\n        try:\n            urllib.request.urlopen(\"http://127.0.0.1:11434/api/tags\", timeout=2)\n            break\n        except Exception:\n            time.sleep(1)\n    print(\"ollama up; pulling\", MODEL, \"(one-time, cached) ...\")\n    subprocess.run([\"ollama\", \"pull\", MODEL], check=True)\n    print(\"model ready:\", MODEL)"
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": "# D2. Start fak in front of Ollama.\nif HAS_GPU:\n    stop_process(\"fak_model_proc\")\n    fak_model_proc = subprocess.Popen(\n        [FAK, \"serve\", \"--addr\", \"127.0.0.1:8080\",\n         \"--base-url\", \"http://127.0.0.1:11434/v1\",\n         \"--model\", MODEL, \"--policy\", POL],\n        cwd=FAK_DIR, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)\n    for _ in range(60):\n        try:\n            with urllib.request.urlopen(\"http://127.0.0.1:8080/healthz\", timeout=2) as r:\n                print(\"fak serve:\", r.read().decode().strip())\n                break\n        except Exception:\n            time.sleep(1)\n    else:\n        raise RuntimeError(\"fak serve did not become healthy on :8080\")"
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": "# D3. Drive it with the OpenAI SDK.\nif HAS_GPU:\n    subprocess.run([sys.executable, \"-m\", \"pip\", \"install\", \"-q\", \"openai\"], check=True)\n    from openai import OpenAI\n    client = OpenAI(base_url=\"http://127.0.0.1:8080/v1\", api_key=\"not-needed\")\n    resp = client.chat.completions.create(\n        model=MODEL,\n        temperature=0.2,\n        messages=[{\"role\": \"user\", \"content\": \"In one sentence, what is a syscall?\"}],\n    )\n    print(\"model through fak:\", resp.choices[0].message.content)\n    models = json.load(urllib.request.urlopen(\"http://127.0.0.1:8080/v1/models\", timeout=10))\n    print(\"\\n/v1/models:\", json.dumps(models, indent=2)[:900])"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": "## Where to go next\n\n- **Tier 2 - the fused in-kernel model** (`fak serve --engine inkernel`, real SmolLM2 via\n  `scripts/fetch-model.sh`): see **`fak-inkernel.ipynb`** in this folder, or\n  `GETTING-STARTED.md` section 4.\n- **Policy authoring:** `POLICY.md` \u00b7 **Architecture:** `ARCHITECTURE.md`\n- **What's real vs. simulated vs. stub:** `CLAIMS.md` (every claim machine-tagged)\n\n*Cleanup* - background servers die when the notebook runtime recycles. To stop them now:\n`for p in [fak_api_proc, fak_model_proc, ollama_proc]: p.terminate()`"
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python"
  },
  "colab": {
   "provenance": []
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
