> ## Documentation Index
> Fetch the complete documentation index at: https://docs.hexgate.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Quickstart

> Watch a role + arguments decide a tool call — first offline, then in your own agent.

<Steps>
  <Step title="Install the package">
    ```bash theme={null}
    pip install hexgate
    ```

    Or, for a local checkout:

    ```bash theme={null}
    git clone https://github.com/HexamindOrganisation/hexgate.git
    cd hexgate
    pip install -e .
    ```
  </Step>

  <Step title="See it enforce — no API keys needed">
    Save this policy as `policy.yaml`. It gives two roles different limits on the
    **same** `refund_order` tool:

    ```yaml theme={null}
    version: 1
    roles:
      support:                       # front-line: small USD refunds only
        default_policy: { mode: deny }
        tools:
          refund_order:
            mode: allow
            constraints:
              - args.amount <= 50
              - args.currency == "USD"
      billing:                       # larger refunds, major currencies
        default_policy: { mode: deny }
        tools:
          refund_order:
            mode: allow
            constraints:
              - args.amount <= 500
              - args.currency in ["USD", "EUR"]
    ```

    Ask Hexgate to decide the **same \$400 refund** for each role. `hexgate policy
            test` evaluates a policy offline — no model, no API keys:

    ```bash theme={null}
    hexgate policy test policy.yaml --role support \
        --tool refund_order --args '{"amount": 400, "currency": "USD"}'
    # ✗ DENY · support → refund_order({"amount": 400, "currency": "USD"})
    #   reason: Policy on "refund_order" denied: constraint failed — args.amount <= 50

    hexgate policy test policy.yaml --role billing \
        --tool refund_order --args '{"amount": 400, "currency": "USD"}'
    # ✓ ALLOW · billing → refund_order({"amount": 400, "currency": "USD"})
    ```

    Same tool, same request — **the caller's role and the arguments decide.** The
    limits live in the policy, outside the model, so a confused or prompt-injected
    agent can't raise its own cap.
  </Step>

  <Step title="Enforce it in a live agent">
    Now put the same policy in front of a real agent. Build one with
    `create_agent`, apply the policy with `enforce_policy`, and run the **same
    request** under two roles — the role comes from the `HexgateContext` scope:

    ```python theme={null}
    import asyncio
    from hexgate import agent_tool, create_agent, enforce_policy, stream_agent, HexgateContext


    @agent_tool(name="refund_order")
    async def refund_order(order_id: str, amount: float, currency: str = "USD") -> str:
        """Issue a refund against an order."""
        return f"Refunded {amount} {currency} on {order_id}"

    agent, handler = create_agent(
        model="openai:gpt-5.4",
        tools=[refund_order],
        system_prompt="You are a support agent. Use refund_order to issue refunds.",
    )
    agent = enforce_policy(agent, "policy.yaml")   # the role-aware policy from step 2

    async def refund_as(role: str):
        print(f"\n[{role}]")
        async with HexgateContext(user_id="u-1", user_roles=[role]):
            async for event in stream_agent(agent, handler, "Refund $400 on order O-42"):
                ...  # render events however you like

    asyncio.run(refund_as("support"))   # refund_order blocked → model sees [policy_denied]
    asyncio.run(refund_as("billing"))   # refund_order allowed → the refund runs
    ```

    ```bash theme={null}
    export OPENAI_API_KEY=sk-...
    python support_demo.py
    ```

    Same agent, same request — under `support` the model's `refund_order($400)`
    call is **blocked** by the policy (over the \$50 cap) and it receives a
    `[policy_denied]` marker it can recover from; under `billing` it goes through.
    That's `enforce_policy` + the per-call `HexgateContext` doing the work — no platform, no
    extra services.

    <Note>
      **Already have an agent?** You don't rewrite it — you wrap it. An OpenAI Agents
      SDK / LangChain / Google ADK / Pydantic AI agent is gated with a single
      adapter call (e.g. `HexgateRunner` is a drop-in for `agents.Runner`). See
      [Framework adapters](/adapters/overview).
    </Note>
  </Step>
</Steps>

## Next steps

<CardGroup cols={2}>
  <Card title="Wrap your framework's agent" icon="puzzle-piece" href="/adapters/overview">
    OpenAI Agents, LangChain/LangGraph, Google ADK, Pydantic AI — same one-wrapper pattern.
  </Card>

  <Card title="Author a policy" icon="shield" href="/policy/yaml-shape">
    Roles, tools, modes, argument constraints, inheritance — the full spec.
  </Card>

  <Card title="Request context + roles" icon="user" href="/concepts/user-scope">
    How the end user's identity + role reach the decision at call time.
  </Card>

  <Card title="Try the terminal REPL" icon="terminal" href="/cli/chat">
    `hexgate chat --agent example_agent` — a local agent with inline decision panels.
  </Card>

  <Card title="Go remote with Hexgate Cloud" icon="cloud" href="/platform/hosted">
    Remote policy enforcement + audit with zero infra — get a key, set one env var.
  </Card>

  <Card title="Pick a path" icon="route" href="/two-paths">
    Local `hexgate chat` vs platform-backed `hexgate serve` — decide here.
  </Card>
</CardGroup>
