# Official Gateman Go client

The official Gateman Go client is a source-distributed implementation of the
authenticated transport and authentication boundary defined by [Contract v1](https://docs.getgateman.com/contract-v1/).
PHP, Node.js, Python, and Go are equally official Gateman clients.

Client version: 1.0.0.

## Requirements and acquisition

The module declares Go 1.26 and has zero third-party runtime dependencies.
Gateman distributes the source archive from:

```text
GET https://getgateman.com/client/go
```

The archive contains go.mod and the production files auth.go, client.go,
errors.go, and response.go. It is not evidence of publication to a Go module
proxy or public repository.

The declared module path is local.gateman/client/go. Incorporate the extracted
module with a local replacement:

```bash
mkdir -p third_party/gateman-go-client
curl --fail --silent --show-error https://getgateman.com/client/go -o /tmp/gateman-go-client.tar.gz
tar -xzf /tmp/gateman-go-client.tar.gz --strip-components=1 -C third_party/gateman-go-client
go mod edit -replace=local.gateman/client/go=./third_party/gateman-go-client
go get local.gateman/client/go
```

Import it using the declared local module path:

```go
import gateman "local.gateman/client/go"
```

The exported release identity is gateman.Version, which is 1.0.0.

## Configuration and initialization

Config contains BaseURL, AppID, PrivateKey, AppSecret, and Environment.
Configuration is explicit; the client does not read environment variables or
files.

BaseURL must be an http or https origin without a path, query, fragment, user
information, or opaque URL form. AppID is required. PrivateKey is canonical
standard Base64 containing a raw 32-byte Ed25519 seed and takes precedence over
AppSecret. Invalid key material is a configuration error and does not fall
back to HMAC.

Environment, when supplied, must be production, staging, or development. The
client does not select a default.

```go
package main
```

```go
import (
    "context"
    "fmt"
    gateman "local.gateman/client/go"
)
```

```go
func main() {
    client, err := gateman.NewClient(gateman.Config{
        BaseURL:   "https://getgateman.com",
        AppID:     "app-id-from-gateman",
        AppSecret: "application-secret-from-gateman",
    })
    if err != nil {
        panic(err)
    }
    response, err := client.Request(
        context.Background(), "/client/get", nil, gateman.RequestOptions{},
    )
    if err != nil {
        panic(err)
    }
    if response.Status >= 200 && response.Status < 300 {
        fmt.Printf("%v\n", response.JSON)
    }
}
```

POST /client/get requires client:read. The values shown are placeholders, not
credentials. A nil payload is serialized as {}.

## Public API

NewClient(config Config) (*Client, error)

Validates configuration and returns a reusable Client. Client is safe for
concurrent use.

Client.Request(ctx context.Context, path string, payload any,
options RequestOptions) (*Response, error)

ctx must be non-nil. A nil ordinary payload becomes {}; other JSON values are
accepted on ordinary routes. Environment-aware routes require map[string]any.
The exact bytes produced by json.Marshal are signed and sent unchanged.

Paths must begin with /. Queries, fragments, controls, dot segments, and
transport-normalizing forms are rejected. Percent-encoded paths remain valid
when transmitted unchanged.

RequestOptions contains:

```go
IdempotencyKey string
CredentialRequestID string
```

IdempotencyKey is trimmed around spaces and tabs, omitted when empty, limited
to 255 UTF-8 bytes, and signed. It does not itself provide route-level
idempotency. CredentialRequestID is valid only for /secrets/get and /db/get;
those routes generate a fresh lowercase 32-hex-character ID when it is empty.

The request timeout is fixed at ten seconds. Caller cancellation and an
earlier caller deadline remain effective.

Client.Health(ctx context.Context) (*Response, error)

Performs unauthenticated GET /health with a three-second timeout. It does not
validate application authorization or provider configuration.

## Environment handling

The client receives the application environment from `Config.Environment`. It
accepts `production`, `staging`, and `development`, and trims and lowercases
the configured value. Requests to routes that require an environment return a
local configuration error when it is absent or invalid; the normalized value
is injected automatically. See [Contract v1](https://docs.getgateman.com/contract-v1/)
for the authoritative route classification.

If a payload already supplies `environment`, its normalized value must match
the configured value or the request returns a validation error. The top-level
map is copied before injection. Environment-independent routes receive no
automatic environment, and no implicit default is selected.

## Responses and errors

Response contains:

```go
Status int
Headers http.Header
RawBody []byte
JSON any
JSONPresent bool
RequestID string
RetryAfter *int
DecodeError error
```

Every received HTTP response, including non-2xx responses, is returned with
status, complete headers, and raw body. Non-empty bodies set JSONPresent.
Valid JSON is stored in JSON; JSON null remains distinguishable from an empty
body through JSONPresent. Malformed JSON leaves RawBody available and sets
DecodeError.

RequestID is populated only when X-Gateman-Request-ID is exactly 16 lowercase
hexadecimal characters. Headers still preserve the original header collection,
including an invalid raw request-ID value. RetryAfter is set for a decimal
integer Retry-After value.

Local failures return *gateman.Error. Error.Kind is one of configuration,
validation, serialization, transport, timeout, or decode. Underlying causes
are available through Unwrap().

Redirects are rejected and requests are never retried automatically.

The shared wire-level signing and authentication rules are defined by [Contract v1](https://docs.getgateman.com/contract-v1/).
