# Route this codebase through the str.ing gateway

You are an agent reading this document. Your job: make this repository send its
LLM calls through the str.ing gateway instead of straight to the model
provider, so the calls are routed, cached, tagged, and visible in the str.ing
dashboard — **without changing what the code does**.

Work through the steps below. Each one states the outcome and a success
criterion you can check; pick the commands and edits that fit this repo (npm or
pnpm, Python or TypeScript, an existing client or a new one). Show the user a
diff before you apply changes.

## Already onboarded? Stop.

First check whether this repo already routes through the gateway. If every LLM
client's base URL already points at the org-scoped proxy under
`https://gateway.str.ing` and the calls already send an `Authorization`
bearer header and a `X-String-Tag` header, then **there is nothing to do**.
Make no changes and tell the user the repo is already onboarded. Re-running this
doc on an onboarded repo is a no-op — that is by design.

## Step 1 — Authenticate

**Outcome:** this machine holds a str.ing API key you never have to see or
handle as plaintext.

**Preferred — the `str` CLI (device-code flow).** If the `str` CLI is
installed, run:

```
str login
```

It prints a short code and opens a browser; the human approves the request
there. The key is written to the system keyring — the agent never receives a
raw long-lived secret on the command line or through the environment.

The CLI ships as `@usestring/cli` (binary `str`). It may not be on the public
npm registry yet — its license is still pending — so `str login` is the path
to prefer *when the command exists*, not one to assume is installed. If `str`
is not available, use the fallback.

**Fallback — a dashboard key.** If `str` is not installed, ask the human to:

1. Open the str.ing dashboard (https://str.ing) and go to **Settings → API Keys**.
2. Create an organization API key (it begins with `nf_sk_`).
3. Export it into the shell as `STRING_API_KEY`, e.g. `export STRING_API_KEY=...`.

Do this yourself only if the human hands you the value; **never print, echo,
paste, or commit the key**. From here on, code and commands read it from the
`STRING_API_KEY` environment variable — the literal value never appears in the
repo or in your output.

**Success criterion:** `str login` reported success, **or** `STRING_API_KEY`
is set in the environment (`test -n "$STRING_API_KEY"`). Never verify a key by
printing it.

## Step 2 — Find the call sites

**Outcome:** a list of every place this repo constructs an LLM client or makes a
provider HTTP call.

Search the codebase for:

- **OpenAI** — `OpenAI(`, `new OpenAI(`, `base_url` / `baseURL`,
  `api.openai.com`.
- **Anthropic** — `Anthropic(`, `new Anthropic(`, `api.anthropic.com`.
- **LangChain** — `ChatOpenAI`, `ChatAnthropic`, `openai_api_base`,
  `base_url=`.
- Raw HTTP calls to `api.openai.com` / `api.anthropic.com`.

**Success criterion:** you can name each file and line that talks to a model
provider. If there are none, say so and stop — there is nothing to route.

## Step 3 — Rewrite each call site

**Outcome:** every client points at the org-scoped gateway, authenticates from
the environment, and labels its traffic.

Per call site, change three things:

1. **Base URL** → the org-scoped proxy:

   ```
   https://gateway.str.ing/{org_slug}/proxy
   ```

   Get `{org_slug}` from the URL of the user's dashboard (https://str.ing/{org_slug}/...).
   The base URL is **org-scoped and ends in `/proxy`** — an OpenAI SDK appends
   `/chat/completions` to it. Do **not** use a bare `/v1` path and do **not**
   drop the org slug; `https://gateway.str.ing/{org_slug}/proxy` is the whole base.
   `https://api.str.ing/{org_slug}/proxy` is the same route under a different host if
   you prefer it.

2. **Authorization** → read the key from the environment, not a literal:

   ```
   Authorization: Bearer $STRING_API_KEY
   ```

3. **Tag** → add a per-call-site label so every gateway number can split by it:

   ```
   X-String-Tag: <a short stable name for this call site>
   ```

   Use one tag per call site (e.g. `summarizer`, `classifier`), not one per
   model. Keep it stable so the dashboard can group traffic over time.

The model IDs and request/response shapes do not change — the gateway is
OpenAI-compatible.

**Make these edits yourself** — there is no codemod, because rewriting real
call-sites needs judgement about shapes no enumeration covers. If you want the
long form, the `str-gateway-onboarding` **Skill** ships inside the
`@usestring/cli` package (`skills/str-gateway-onboarding/SKILL.md`) with
per-SDK constructor shapes, which shapes to defer on, and how to tell an
already-onboarded call site from a new one. This document stands alone without
it.

**Success criterion:** every call site you found in Step 2 now uses the gateway
base URL above, sends `Authorization: Bearer $STRING_API_KEY`, and sets a
`X-String-Tag` header. No provider base URL (`api.openai.com`,
`api.anthropic.com`) remains except where you deliberately left one.

## Step 4 — Verify with one real call

**Outcome:** proof the gateway actually served a request from this repo — not
just that the code compiles.

Make **one** real request. Either run a small script through a client you
rewired, or call the gateway directly:

```
curl -sS -X POST https://gateway.str.ing/{org_slug}/proxy/chat/completions \
  -H "Authorization: Bearer $STRING_API_KEY" \
  -H "X-String-Tag: install-md-verify" \
  -H "Content-Type: application/json" \
  -d '{"model":"openai/gpt-4o","messages":[{"role":"user","content":"ping"}]}'
```

Then open the dashboard, go to **Workloads**, and open the **Requests** tab — its
table lists recent proxy calls. Find the one you just made (filter by the
`X-String-Tag` value you used, e.g. `install-md-verify`).

**Success criterion:** the call returned a normal chat-completion response
**and** it appears in the Requests table. If it returned but does not
appear, the base URL or org slug is wrong — recheck Step 3. This is the step
that separates "the agent claims it worked" from "it worked."

## Step 5 — Persist the convention (optional)

**Outcome:** future agent sessions keep routing through the gateway without
re-reading this doc.

A one-time edit only fixes the call sites that exist today; the next session,
in a fresh context, can add a new client that skips the gateway entirely.
Offer to record the convention where every later agent session will read it.

Detect `AGENTS.md` and `CLAUDE.md` at the repo root. Offer whichever exists;
write to both if both exist; if neither exists, offer to create `AGENTS.md`.
**Only write if the user agrees — this is someone else's file.**

Put the block between markers so a later run can find and replace it instead of
appending a duplicate:

```
<!-- str.ing:begin -->
## str.ing gateway
- Base URL: `https://gateway.str.ing/{org_slug}/proxy` (org-scoped, no `/v1`)
- Key: read `STRING_API_KEY` from the environment — never a literal
- Tag every call: `X-String-Tag: <call-site-name>`
<!-- str.ing:end -->
```

`{org_slug}` above is a template placeholder — write the real, resolved org
slug (from Step 3) into the block; a literal `{org_slug}` written into the repo is
a dead placeholder, not a value.

If the markers are already present, replace everything between them — re-running
this step must never append a second copy.

---

**Done.** Summarize for the user: which call sites you changed, the verification
result, and whether you wrote to `AGENTS.md`. If the repo was already onboarded,
say that instead and report that you made no changes.
