# agentrouter-go

Hand-written Go SDK for the **Tetrate Agent Router (TARS)**
control plane. A thin, idiomatic wrapper over the buf-generated
**Connect-Go** clients under `api/tars/*/v1/*v1connect`.

Module: `github.com/tetrateio/agentrouter-go`. The downloaded tarball is
self-contained: the generated Connect-Go bindings are bundled under `genapi/`
with their imports rewritten, so there is no dependency on any parent module.

## Install (from a downloaded tarball)

Download the Go SDK tarball from the AgentRouter **SDKs page** (`/docs/sdks`
in the dashboard) -- the download is session-gated, and the Downloads table
lists the version and SHA-256 for each artifact. We do not publish to the
public Go module proxy; consumption is via a `replace` directive pointing at
the extracted source tree.

1. Extract the tarball (replace `<version>` with the version shown on the
   SDKs page):

   ```
   mkdir -p third_party && tar -xzf agentrouter-go-<version>.tar.gz -C third_party/
   ```

   Optionally verify its SHA-256 against the digest shown on that page:

   ```
   shasum -a 256 agentrouter-go-<version>.tar.gz
   ```

2. In your own `go.mod`, redirect the module path at the extracted
   source:

   ```
   require github.com/tetrateio/agentrouter-go v0.1.0

   replace github.com/tetrateio/agentrouter-go => ./third_party/agentrouter-go-<version>
   ```

3. Resolve transitive dependencies:

   ```
   go mod tidy
   ```

The `replace` line is the only customer-facing install mechanism: the
SDK is not published to the public Go module proxy. See the SDKs page
(`/docs/sdks`) for downloads and `/docs/api-reference/install` for the
full install guide.

## Usage

```go
import (
    "context"
    "os"

    agentrouter "github.com/tetrateio/agentrouter-go"
)

ctx := context.Background()

// Production: one facade base URL + an API key.
c, _ := agentrouter.New(ctx,
    agentrouter.WithBaseURL(os.Getenv("AGENTROUTER_BASE_URL")),
    agentrouter.WithAPIKey(os.Getenv("AGENTROUTER_API_KEY")),
)
providers, _ := c.Catalog().ListProviders(ctx)
me, _       := c.Me().Get(ctx)
proj, _     := c.Projects().Create(ctx, "acme", "platform", "ML Platform", "")
reply, _    := c.Inference().Prompt(ctx, "acme", "platform", "Hello!", agentrouter.WithModel("openai/gpt-4o-mini"))

// Local dev: route each service to its own URL.
c2, _ := agentrouter.New(ctx,
    agentrouter.WithSessionToken(os.Getenv("AGENTROUTER_SESSION_TOKEN")),
    agentrouter.WithEndpoints(agentrouter.Endpoints{
        Identity:  "http://localhost:9091",
        Tenancy:   "http://localhost:9092",
        Catalog:   "http://localhost:9100",
        Inference: "http://localhost:9097",
    }),
)
```

## Surface

Service handles on `Client`, covering every unary RPC the control plane
exposes under `api/tars/`:

- `Me()` -- `Login`, `Logout`, `Get`, `IssueKey`, `ListKeys`, `RevokeKey`
- `Identity()` -- `ResolveAPIKey` (internal)
- `Customers()` / `Tenancy()` -- `Create`, `Get`, `List`, `Update`, `Archive`
- `Projects()` -- `Create`, `Get`, `List`, `Update`, `Archive`,
  `AddMember`, `ListMembers`, `UpdateMember`, `RemoveMember`
- `Catalog()` -- `ListProviders`, `GetProvider`, `ListModels`, `GetModel`,
  `UpsertProvider`, `UpsertModel`, `SetProviderCredential`,
  `DeleteProvider`, `DeleteModel`, `AssignModelToProject`,
  `ListProjectModels`, `UnassignModelFromProject`
- `AIKeys()` -- `Register`, `Get`, `List`, `Revoke`, `Fetch` (internal)
- `Inference()` -- `Prompt` (with `WithModel`, `WithKeyID`,
  `WithIdempotencyKey` options)
- `Clients()` -- `Create`, `List`, `Get`, `UpdateTitle`, `AddKey`,
  `RotateKey`, `DisableKey`, `EnableKey`, `DestroyKey`, `EmergencyRevoke`
- `Telemetry()` -- `Create`, `List`, `Get`, `Update`, `Delete`
- `MCP().Profiles()` -- `Create`, `Get`, `List`, `Update`, `Delete`
- `MCP().Catalog()` -- `Upsert`, `Get`, `List`, `SetEnabled`, `Delete`

## Conventions

- **`context.Context` first** on every RPC; **`(T, error)`** returns.
- **Functional options** for construction (`WithBaseURL`, `WithAPIKey`,
  `WithSessionToken`, `WithEndpoints`, `WithRetryPolicy`,
  `WithHTTPClient`, `WithUserAgent`).
- **Typed errors:** failures return `*agentrouter.Error` (`errors.As`) with
  `.Code` (the canonical Connect code) and `.Reason` (the machine-readable
  RFC 7807 reason from the `google.rpc.ErrorInfo` detail the platform attaches
  -- e.g. `MODEL_NOT_FOUND`, `CROSS_TENANT_DENIED`; `""` when absent):

  ```go
  var fe *agentrouter.Error
  if errors.As(err, &fe) {
      switch fe.Reason {
      case "MODEL_NOT_FOUND":
          // handle a bad model selector
      case "CROSS_TENANT_DENIED":
          // handle a cross-tenant access denial
      default:
          // fall back to fe.Code
      }
  }
  ```
- **Auth:** `WithAPIKey` sets both `Authorization` and `X-Api-Key`;
  `WithSessionToken` sets `Authorization: Bearer ...`. Mutually
  exclusive.
- **Retry:** transient codes (Unavailable, DeadlineExceeded, Aborted)
  retried with backoff; override via `WithRetryPolicy`.

## Tests

```
go test ./...
```

## Quickstart

The SDK tarball bundles a runnable quickstart at `examples/quickstart`. From the
SDK root:

```
go run ./examples/quickstart
```

It uses a `replace github.com/tetrateio/agentrouter-go => ../..` onto the bundled
SDK, so it builds against the bundled bindings with no further setup.

## Codegen

The proto bindings the SDK depends on are generated by the parent
module's `make api.generate.golang` target. The SDK itself is hand-
written -- `make sdk.generate` simply re-runs the parent template so
this directory always sees the latest connect-go bindings.
