# Your first governed agent

> Run a safe tool, see Thoth stop a disallowed action, and inspect both decisions.

Source: https://docs.atensecurity.com/docs/getting-started/quickstart

Start with two local callbacks: one returns a greeting, the other must never
execute. You do not need a model API key or an existing agent framework.
After this guide, you will have checked an action before execution and
verified the result in your tenant's decision history.

**Before you start:**
  You need an activated trial with the `trial-demo` policy, your tenant ID,
  unified tenant API base URL, and execute key. [Trial access and connection
  details](https://docs.atensecurity.com/docs/getting-started/trial-quickstart) explains how to get them.
  These examples are not an offline demo; they call your tenant's enforcer.

## 1. Set your connection details

In a local terminal, set these variables using your trial details. Load the key
from your secret manager or a private file; do not commit it or paste it into
shared terminal logs.

```bash title="Terminal"
export THOTH_TENANT_ID="<your-trial-tenant-id>"
export THOTH_API_URL="<your-unified-tenant-api-base-url>"
export THOTH_API_KEY="$(cat /absolute/private/path/trial-execute-key)"
export THOTH_FAIL_OPEN=false
```

## 2. Install the SDK and run your agent

Use Python 3.12+, Node.js 22+, or Go 1.25+. These examples need no model API
key and make no external tool calls. They use the same `trial-demo` agent and
tool names as the onboarding demo. Start with `demo.read`; only proceed to
`shell.exec` after the read succeeds.

The trial demo policy is expected to allow `demo.read` and block `shell.exec`.
`approved_scope` describes the agent's intended tools; it does not install or
override server policy. The `shell.exec` callback is a guard that raises an
error if reached; it never starts a shell.

**Python**

```bash title="Terminal"
python3 -m venv .venv
source .venv/bin/activate
python -m pip install atensec-thoth==0.5.23
```

Save as `trial.py`, then run `python trial.py`:

```python title="trial.py"
import os
from thoth import instrument, ThothPolicyViolation


class Tool:
    def __init__(self, name, fn):
        self.name = name
        self.run = fn


def forbidden_shell():
    raise RuntimeError("Unexpected execution: inspect the trial demo policy")


class Agent:
    def __init__(self):
        self.tools = [
            Tool("demo.read", lambda: "Hello from the trial demo"),
            Tool("shell.exec", forbidden_shell),
        ]


agent = instrument(
    Agent(),
    agent_id="trial-demo",
    tenant_id=os.environ["THOTH_TENANT_ID"],
    api_url=os.environ["THOTH_API_URL"],
    api_key=os.environ["THOTH_API_KEY"],
    approved_scope=["demo.read"],
    user_id="trial-developer",
    environment="prod",
    enforcement="progressive",
)

# Any failure here stops the demo before the denied tool is attempted.
print(agent.tools[0].run())
try:
    agent.tools[1].run()
except ThothPolicyViolation as refusal:
    print(f"Tool refused: {refusal}")
else:
    raise RuntimeError("Expected shell.exec to be refused")
```

**TypeScript**

```bash title="Terminal"
npm init -y
npm pkg set type=module
npm install @atensec/thoth@0.1.21
npm install --save-dev tsx typescript @types/node
```

Save as `trial.ts`, then run `npx tsx trial.ts`:

```typescript title="trial.ts"
import {
  instrument,
  EnforcementMode,
  ThothPolicyViolation,
} from "@atensec/thoth";

function required(name: string): string {
  const value = process.env[name];
  if (!value) throw new Error(`Set ${name} before running the demo`);
  return value;
}

async function main() {
  const agent = instrument(
    {
      tools: [
        { name: "demo.read", run: async () => "Hello from the trial demo" },
        {
          name: "shell.exec",
          run: async () => {
            throw new Error("Unexpected execution: inspect the trial demo policy");
          },
        },
      ],
    },
    {
      agentId: "trial-demo",
      tenantId: required("THOTH_TENANT_ID"),
      apiUrl: required("THOTH_API_URL"),
      apiKey: required("THOTH_API_KEY"),
      approvedScope: ["demo.read"],
      userId: "trial-developer",
      environment: "prod",
      enforcement: EnforcementMode.PROGRESSIVE,
      failOpen: false,
    },
  );

  console.log(await agent.tools[0].run());
  try {
    await agent.tools[1].run();
  } catch (refusal) {
    if (!(refusal instanceof ThothPolicyViolation)) throw refusal;
    console.log(`Tool refused: ${refusal.message}`);
    return;
  }
  throw new Error("Expected shell.exec to be refused");
}

main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});
```

**Go**

```bash title="Terminal"
go mod init trial-demo
go get github.com/atensecurity/thoth-go@v0.1.19
```

Save as `main.go`, then run `go run .`:

```go title="main.go"
package main

import (
	"context"
	"errors"
	"fmt"
	"log"
	"os"

	thoth "github.com/atensecurity/thoth-go"
)

func run() error {
	client, err := thoth.NewClient(thoth.Config{
		APIKey:        os.Getenv("THOTH_API_KEY"),
		APIURL:        os.Getenv("THOTH_API_URL"),
		TenantID:      os.Getenv("THOTH_TENANT_ID"),
		AgentID:       "trial-demo",
		ApprovedScope: []string{"demo.read"},
		UserID:        "trial-developer",
		Environment:   "prod",
		Enforcement:   "progressive",
		FailOpen:      false,
	})
	if err != nil {
		return err
	}
	defer client.Close()

	read := client.WrapTool("demo.read", func(context.Context, string) (string, error) {
		return "Hello from the trial demo", nil
	})
	shell := client.WrapTool("shell.exec", func(context.Context, string) (string, error) {
		return "", errors.New("unexpected execution: inspect the trial demo policy")
	})

	result, err := read(context.Background(), "")
	if err != nil {
		return err
	}
	fmt.Println(result)
	_, err = shell(context.Background(), "")
	var refusal *thoth.PolicyViolationError
	if errors.As(err, &refusal) {
		fmt.Printf("Tool refused: %s\n", refusal)
		return nil
	}
	if err != nil {
		return err
	}
	return errors.New("expected shell.exec to be refused")
}

func main() {
	if err := run(); err != nil {
		log.Fatal(err)
	}
}
```

## 3. Verify the result

With the configured demo policy, the terminal prints:

```text title="Output"
Hello from the trial demo
Tool refused: ...shell.exec...demo policy...
```

The refusal's exact wording varies by SDK and policy. Treat a missing greeting,
an unexpected execution error, or a different decision as a failed check.

Confirm the `demo.read` decision is `ALLOW` and the `shell.exec` decision is
`BLOCK` in decision history, including the reason and correlation ID. A
refusal exception alone is insufficient: fail-closed SDKs also refuse calls
on authentication, rate-limit, or transport failures. Resolve those errors
before counting the demo as successful. A `STEP_UP` pauses for approval; it
is not the expected result for this two-tool demo.

The demo `BLOCK` quarantines `trial-demo`. An administrator must open
**Agents → trial-demo → Restore Agent** before repeating the examples,
including switching SDK languages. Then start with `demo.read` again.

Onboarding activation progress records the first authenticated, finalized
enforcement decision. It can record a refused action too. Check decision
history separately to verify the expected policy outcome and available
evidence; the progress indicator does not prove tool execution or a finalized
evidence export.

## 4. Connect your own agent

You have verified a permitted action and a refusal. Next, [instrument your
own agent](https://docs.atensecurity.com/docs/getting-started/your-agent) and keep its tool execution behind
the same policy check. If the demo did not behave as expected, use the
[troubleshooting guide](https://docs.atensecurity.com/docs/getting-started/troubleshooting) before continuing.
