Gateman
Documentation/Canonical Go client

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. 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:

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:

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:

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.

package main
import (
    "context"
    "fmt"
    gateman "local.gateman/client/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:

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 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:

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.