Start free

Authentication

How the SDKs and CLI authenticate to the HESO control plane: an API key sent as a Bearer token, configured once. Local operations — gating, signing, and offline verification — need no key at all.

HESO has two kinds of work. The first is local: capturing an action, evaluating it against policy, signing it, and verifying a receipt. None of that talks to a server, so none of it needs authentication. The second is the cloud: pulling your team’s policy, pushing receipts to the outbox, and opening approvals. Every cloud call carries an API key as a Bearer token. This page covers how to get that key, where to put it, and what genuinely depends on it.

API keys

A HESO API key authenticates one team to the control plane — the HESO servers your team talks to. Create a key in the dashboard under Settings / Billing, then read it from an environment variable. Every request sends it in the Authorization header as a Bearer token, with Content-Type: application/json on calls that send a body.

  • Header shape: Authorization: Bearer <API_KEY>.
  • The base URL is your configured endpoint, not a fixed host.
  • The key scopes requests to a single team; team-scoped routes take the team id in the path.

Here is a plain request to pull a team’s policy bundle, reading the key from the environment:

pull-policy.sh
curl https://api.heso.example/v1/teams/team_19/policy \  -H "Authorization: Bearer $HESO_API_KEY"

A successful pull returns a PolicyBundleversion, rules, and fetchedAt. The SDKs wrap this same request, so you rarely write the curl by hand. See the API reference for the full endpoint list.

Configure the client

Both SDKs read the same key — they just install it differently. In TypeScript you call configure once, with the key and the endpoint, before any cloud call. In Python, heso.init resolves configuration from arguments, environment, and your heso.toml.

import { configure, pullPolicy } from "@hesohq/sdk" // Once, at startup — before any cloud call.configure(process.env.HESO_API_KEY!, process.env.HESO_ENDPOINT!) // Now the cloud client is authenticated.const bundle = await pullPolicy("team_19")console.log(bundle.version, bundle.rules.length)

On the TypeScript side, configure(apiKey, endpoint) sets the credentials for the cloud client. Call it once at startup; every later pullPolicy, pushReceipt, and approval helper uses what you set. The verifying side of the SDK — gate and assertGate — calls @hesohq/core in-process and needs no key.

Order matters

Call configure before the first cloud call. The Python init checks explicit arguments first, then environment variables, then heso.toml, then defaults. Calling init again replaces the active config.

Environment variables

The Python SDK reads its configuration from environment variables when explicit arguments to init are not given. None of these is an API key — they describe the local project and run, not cloud credentials.

VariableWhat it sets
HESO_PROJECT_ROOTProject root used to discover the config and the local data dir.
HESO_WORKFLOWDefault workflow label stamped onto captured actions.
HESO_ACCOUNTDefault account stamped onto captured actions.
HESO_CLOCKClock override for deterministic capture timestamps.
HESO_TIMEOUTTimeout for gate operations.
HESO_BLOCKINGWhether a blocked or suspended action raises (default) or is observe-only.
HESO_BINPath to the Rust engine binary, when not on the default path.

On the TypeScript side there is no env-var convention — the SDK reads whatever key and endpoint you pass to configure. The common pattern is to pull them from process.env, for example process.env.HESO_API_KEY and process.env.HESO_ENDPOINT, as the example above does.

What needs auth

This is the line that matters most: verification never depends on HESO. The crypto runs in your process or in a reviewer’s browser, so checking a receipt needs no key, no account, and no network. Only the cloud endpoints require the Bearer key.

OperationNeeds the API key?
gate / assertGate — verify a receiptNo — runs in-process via @hesohq/core
Signing and hashing an actionNo — local, no network
Offline verification in the browserNo — pure WASM, no infrastructure
pullPolicy — fetch the policy bundleYes
pushReceipt / pushReceipts — outboxYes
openApproval / pollApproval — route to a humanYes
What auth does — and what it doesn’t

An API key authenticates a request to our control plane. It does not give the cloud the power to forge a receipt: the server re-verifies every receipt before accepting it, and a verdict is re-derived from the signatures themselves. Because the cloud holds no signing key, an API key can never produce a valid operator or approver signature. Trust comes from the math, not from being logged in.

Where keys live

The cloud API key is the only secret you pass in — read it from the environment at startup. Everything else is handled for you: heso init writes the local data directory (your operator key, the audit log, the outbox) and ignores it in git. The approver key never leaves the approver’s device. That is the whole point of an L1 co-signature — the cloud can’t hold the key, so it can’t forge it.

Next steps

With auth in place, pull your policy and start pushing receipts.