# Official Gateman Node.js client

The official Gateman Node.js client is an ESM 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 client supports Node.js 22 or newer and has zero runtime dependencies. It
is server-side ESM and does not support browser use, Deno, or CommonJS.

Gateman distributes a compiled local package archive from:

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

The archive contains dist/src, declaration files, package.json, and README.
It is not evidence of npm registry publication. Install it as a local npm
dependency:

```bash
mkdir -p vendor
curl --fail --silent --show-error https://getgateman.com/client/node -o /tmp/gateman-node-client.tar.gz
tar -xzf /tmp/gateman-node-client.tar.gz --strip-components=1 -C vendor
npm install ./vendor
```

The package identity is @getgateman/client.

## Configuration and initialization

GatemanClientConfig contains baseUrl, appId, privateKey, appSecret, and
environment. Configuration is explicit; the client does not load .env files or
read process.env automatically.

baseUrl must use http or https and contain no query or fragment. appId is
required. privateKey is canonical standard Base64 containing a raw 32-byte
Ed25519 seed. It takes precedence over appSecret. Invalid private-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.

```javascript
import { GatemanClient } from '@getgateman/client';
```

```javascript
const client = new GatemanClient({
  baseUrl: 'https://getgateman.com',
  appId: 'app-id-from-gateman',
  appSecret: 'application-secret-from-gateman',
});
```

```javascript
const response = await client.request('/client/get', {});
if (response.status >= 200 && response.status < 300) {
  console.log(response.json);
}
```

POST /client/get requires client:read. The values shown are placeholders, not
credentials.

## Public API

new GatemanClient(config)

Constructs a client and validates the URL, application ID, environment, and
authentication material. Invalid configuration throws GatemanClientError with
kind configuration.

client.request<T>(path, payload?, options?)

```javascript
request<T = unknown>(
  path: string,
  payload: unknown = {},
  options: GatemanRequestOptions = {},
): Promise<GatemanResponse<T>>
```

The payload must be a non-null object and cannot be an array. The method sends
one authenticated JSON POST and never retries.

GatemanRequestOptions contains:

```javascript
idempotencyKey?: string
credentialRequestId?: string
timeoutMs?: number
```

The idempotency key 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 omitted. timeoutMs must be finite and greater than zero; the default is
10,000 milliseconds.

Paths must begin with / and may not contain queries, fragments, controls,
unencoded spaces, Unicode, dot segments, or other transport-normalizing forms.
Already percent-encoded transport-equivalent paths are accepted.

client.health()

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

## Environment handling

The client receives the application environment through the configuration
object’s `environment` property. It accepts `production`, `staging`, and
`development`, and trims and lowercases the value during initialization.
Requests to routes that require an environment fail locally when no valid
environment is configured; the normalized value is injected automatically.
See [Contract v1](https://docs.getgateman.com/contract-v1/) for the
authoritative route classification.

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

## Responses and errors

GatemanResponse<T> contains:

```javascript
status: number
headers: Headers
rawBody: string
json?: T
requestId?: string
retryAfter?: number
decodeError?: GatemanClientError
```

All received HTTP responses, including non-2xx statuses, resolve normally.
status is authoritative. rawBody is the body decoded as UTF-8. Valid JSON is
available as json. A non-empty malformed JSON body remains in rawBody and sets
decodeError with kind decode.

requestId is populated only for an exactly 16-character lowercase hexadecimal
X-Gateman-Request-ID. retryAfter is populated for a valid safe integer
Retry-After value. Local failures throw GatemanClientError with kind
configuration, validation, serialization, transport, timeout, or decode.

Redirects are rejected as transport failures. Requests are never retried
automatically.

The shared wire protocol is defined by [Contract v1](https://docs.getgateman.com/contract-v1/).
