Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ graph LR

## 📄 Configuration overview

AuthTranslator eats **YAML** (or pure JSON) for two files:
AuthTranslator eats **YAML** (or pure JSON) for three files:

| File | Purpose |
| ---------------- | ----------------------------------------------------------------------------------------------------- |
Expand All @@ -84,7 +84,7 @@ Secrets can be pulled from several providers:
* **file:** path to an on‑disk file
* **k8s:** Kubernetes secrets
* **gcp:** Google Cloud KMS
* **aws:** AWS Secrets Manager
* **aws:** AES-GCM encrypted values using `AWS_KMS_KEY`
* **azure:** Azure Key Vault
* **vault:** HashiCorp Vault

Expand Down Expand Up @@ -112,7 +112,7 @@ Also see [`cmd/allowlist`](cmd/allowlist) for CRUD operations on the allowlist.
| Endpoint | Purpose |
| ----------------------- | ---------------------------------------------------------------------- |
| `/_at_internal/healthz` | Liveness probe – returns **200 OK** when the proxy is running. |
| `/_at_internal/metrics` | Prometheus metrics (request totals, status codes, latency, ratelimit and auth failures plus Go runtime metrics). |
| `/_at_internal/metrics` | Prometheus metrics for request totals, status codes, latency, rate-limit events, auth failures, and proxy-generated responses. |
| Structured logs | Text by default; pass `-log-format json` for JSON via `slog`. Includes method, integration, path, status; adds `caller_id` when known. |

Official container images include a Docker HEALTHCHECK that polls the health endpoint; the container reports **healthy** once it returns 200.
Expand Down
6 changes: 4 additions & 2 deletions docs/allowlist-yaml.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Unknown top‑level keys cause a validation error.
## 1 Caller ID keys

* **Exact ID** `user-123`, `service-A`, `spiffe://tenant/worker`
* **Wildcard** `"*"` – used when the incoming auth plugin did **not** return an ID. Handy for anonymous webhooks.
* **Wildcard** `"*"` – used when the incoming auth plugin did **not** return an ID, or as a fallback for caller IDs without an explicit entry. Handy for anonymous webhooks and broad defaults.

If no matching caller key exists – or a matched caller fails rule constraints – the proxy returns **403 Forbidden** and sets an `X-AT-Error-Reason` header describing the first mismatch. Every 4xx/5xx error from the proxy includes this header with a brief phrase explaining the reason.

Expand Down Expand Up @@ -51,6 +51,8 @@ Look for a `capabilities.go` file under `app/integrations/plugins/<integration>/
- id: bot-123
capabilities:
- name: post_as
params:
username: AuthTranslator
```
Each capability item contains a `name` and optional `params` map.

Expand Down Expand Up @@ -122,4 +124,4 @@ A request passes if **any** rule (or capability‑expanded rule) matches.

* **One capability ≈ one business use‑case** (e.g. `post_as`).
* Prefer **uppercase** HTTP methods (`GET`, `POST`) for consistency.
* Log level `debug` will print which rule matched; helpful in staging.
* Use `X-AT-Error-Reason` on 403 responses to see why a request missed the allowlist or failed constraints.
10 changes: 5 additions & 5 deletions docs/auth-plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

AuthTranslator’s behaviour is extended by **plugins** – small Go packages that validate the *incoming* caller credential or inject the *outgoing* credential expected by an upstream service.

* **Incoming plugins** run **before** any allowlist or denylist checks. They must either
* **Incoming plugins** run **before** any denylist or allowlist checks. Each configured plugin must authenticate the request successfully. Plugins may also implement optional interfaces to derive a caller ID or strip credentials before proxying.

1. **Accept**: return a `callerID` string; the request continues, or
2. **Reject**: return an error → the proxy replies **401/403**.
* **Outgoing plugins** run **after** the allowlist passes and the denylist check succeeds. They mutate the request (headers, query or body) so the upstream authenticates it.
1. **Accept**: return `true`; the request continues to the next incoming plugin.
2. **Reject**: return `false`; the proxy replies **401 Unauthorized**.
* **Outgoing plugins** run after the request avoids the denylist and passes the allowlist. They mutate the request (headers, query or body) so the upstream authenticates it.

> **Tip** Any plugin can be swapped at runtime – just edit `config.yaml` and send `SIGHUP` (or run with `-watch`).

Expand Down Expand Up @@ -201,6 +201,6 @@ Identifiers flow through to:
| |
| - |

* Use `-log-level DEBUG` to dump request headers after plugin injection (redacted for secrets).
* Use `X-AT-Error-Reason` response headers and WARN logs to distinguish authentication, allowlist, denylist, and rate-limit failures.

---
6 changes: 3 additions & 3 deletions docs/capabilities.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Built-in Capabilities

Each integration plugin can expose **capabilities** – named groups of rules that map to common API actions. Assigning a capability in `allowlist.yaml` expands to the underlying HTTP rules automatically.
Each runtime integration plugin can expose **capabilities** – named groups of rules that map to common API actions. Assigning a capability in `allowlist.yaml` expands to the underlying HTTP rules automatically.

Run `go run ./cmd/allowlist list` to list capabilities from your build. For quick reference, the table below summarises the capabilities bundled with AuthTranslator.
For quick reference, the table below summarises the capabilities bundled into the proxy runtime. The `cmd/allowlist` helper has its own registry for generating YAML, so `go run ./cmd/allowlist list` shows what that helper can emit rather than every runtime capability below.

| Integration | Capability | Parameters |
|-------------|-----------|------------|
Expand Down Expand Up @@ -64,4 +64,4 @@ Run `go run ./cmd/allowlist list` to list capabilities from your build. For quic
| zendesk | update_ticket | – |

For SendGrid `send_email`, if `replyTo` is omitted the reply address is empty. Provide `null` explicitly to leave the reply-to header unset.
Capabilities not listed above may be added by custom plugins. Use the CLI to discover them in your build.
Capabilities not listed above may be added by custom runtime plugins.
33 changes: 16 additions & 17 deletions docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ AuthTranslator ships with two small helper binaries under **`cmd/`**:

| Binary | Purpose | Typical usage |
| -------------- | --------------------------------------------- | ------------------------------------------------- |
| `integrations` | Modify or inspect *config.yaml*. | `go run ./cmd/integrations slack -file config.yaml -token env:SLACK_TOKEN -signing-secret env:SLACK_SIGNING` |
| `allowlist` | Modify or inspect *allowlist.yaml*. | `go run ./cmd/allowlist add -integration slack -caller bot -capability post_as` |
| `integrations` | Modify or inspect *config.yaml*. | `go run ./cmd/integrations -file config.yaml slack -token env:SLACK_TOKEN -signing-secret env:SLACK_SIGNING` |
| `allowlist` | Modify or inspect *allowlist.yaml*. | `go run ./cmd/allowlist -file allowlist.yaml add -integration slack -caller bot -capability post_as` |
These helpers complement the [Configuration Reference](configuration-overview.md) and [Allowlist Configuration](allowlist-yaml.md) docs.

> **Heads‑up** Both helpers are thin wrappers around Go structs—check the `--help` output for the definitive flag list because the CLI evolves alongside the schema.
Expand All @@ -17,8 +17,8 @@ These helpers complement the [Configuration Reference](configuration-overview.md
You can run directly with `go run`, but for faster iteration:

```bash
go install ./cmd/integrations@latest
go install ./cmd/allowlist@latest
go install ./cmd/integrations
go install ./cmd/allowlist
```

Make sure `$GOBIN` is on your `PATH`.
Expand All @@ -28,7 +28,7 @@ Make sure `$GOBIN` is on your `PATH`.
## 2  `integrations` helper

```text
integrations <command> [flags]
integrations [options] <command> [plugin options]
```

### Common commands
Expand All @@ -42,12 +42,11 @@ integrations <command> [flags]

```bash
# Add a Slack integration from env vars
go run ./cmd/integrations slack \
-file config.yaml \
go run ./cmd/integrations -file config.yaml slack \
-token env:SLACK_TOKEN -signing-secret env:SLACK_SIGNING

# Delete an integration
go run ./cmd/integrations delete slack -file config.yaml
go run ./cmd/integrations -file config.yaml delete slack
```

#### Flags
Expand All @@ -59,14 +58,14 @@ go run ./cmd/integrations delete slack -file config.yaml
## 3  `allowlist` helper

```text
allowlist <command> [flags]
allowlist [options] <command> [command flags]
```

### Common commands

| Command | Purpose |
| -------- | ---------------------------------------------------- |
| `list` | Show capabilities provided by integration plugins. |
| `list` | Show capabilities known to the allowlist helper. |
| `add` | Append a capability entry to `allowlist.yaml`. |
| `remove` | Delete an entry from `allowlist.yaml`. |

Expand All @@ -75,11 +74,11 @@ allowlist <command> [flags]
go run ./cmd/allowlist list

# Grant a caller permission
go run ./cmd/allowlist add -integration slack \
go run ./cmd/allowlist -file allowlist.yaml add -integration slack \
-caller bot-123 -capability post_as

# Revoke that permission
go run ./cmd/allowlist remove -integration slack \
go run ./cmd/allowlist -file allowlist.yaml remove -integration slack \
-caller bot-123 -capability post_as
```

Expand All @@ -93,8 +92,8 @@ go run ./cmd/allowlist remove -integration slack \
| `-capability` | – | Capability name for `add`/`remove`. |
| `-params` | "" | Extra `key=value` pairs for `add` (optional). |

`allowlist list` prints the capability names registered by each integration plug-in
and the parameter keys they expect. It does **not** read `allowlist.yaml`; the
`allowlist list` prints the capability names known to the allowlist helper
and the parameter keys it knows about. It does **not** read `allowlist.yaml`; the
command is purely a discovery tool to help you decide which capability name and
parameter keys to pass to `add`.

Expand All @@ -108,15 +107,15 @@ then touch up the generated YAML manually to insert arrays or nested objects.

## 4  Using helpers in CI

A minimal **GitHub Actions** snippet that checks both files on every PR:
A minimal **GitHub Actions** snippet that checks helper-readable config and lists helper-supported capabilities:

```yaml
- name: Validate AuthTranslator config
run: |
go run ./cmd/integrations list
go run ./cmd/integrations -file config.yaml list
go run ./cmd/allowlist list
```

Fail‑fast means broken YAML never reaches production.
The `integrations` helper reads `config.yaml` using the helper's supported integration fields, so use schema or application startup validation for full runtime config coverage.

If you template configs (e.g., with CUE or Helm), call the helpers *after* rendering so you lint the final artifacts.
15 changes: 8 additions & 7 deletions docs/configuration-overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ AuthTranslator loads up to **three** YAML (or pure‑JSON) documents at runtime:
If no allowlist is provided, every request is permitted once inbound authentication succeeds.
Running without an allowlist effectively gives all authenticated callers unrestricted access, so supplying `allowlist.yaml` is **strongly recommended** even if it just contains a single wildcard entry to start. The denylist stays optional as well; omit it when you have no hard blocks to enforce.

The proxy currently infers its schema directly from Go structs. Unknown top‑level keys cause a validation error.
The proxy currently infers its schema directly from Go structs. Unknown YAML fields cause a validation error.

> **Tip** The Go YAML parser accepts JSON too, so curl pipes / CI steps can build your config in whichever syntax is easier to template.
For command-line tooling, see the [Command-Line Helpers](cli.md) guide.
Expand Down Expand Up @@ -52,10 +52,10 @@ See [Secret Back-Ends](secret-backends.md) for all supported URI schemes.
| --------------- | -------------- | ------------ | ---------------------------------------------------------------------------- |
| `destination` | URL | **required** | Base URL; path from client is appended as‑is. Supports `*` wildcards in the host (e.g. `https://*.example.com`) when paired with an `X-AT-Destination` header containing the concrete upstream URL. |
| `outgoing_auth` | `[]PluginSpec` | `[]` | Injects credential **before** forwarding. |
| `incoming_auth` | `[]PluginSpec` | `[]` | Zero or more validators that run **in order**; the first that succeeds wins. |
| `in_rate_limit` | int | `0` | Max inbound requests per caller within the window. |
| `out_rate_limit` | int | `0` | Max outbound requests per caller within the window. |
| `rate_limit_window` | duration | `1m` | Rolling window length for rate limiting. |
| `incoming_auth` | `[]PluginSpec` | `[]` | Zero or more validators that run **in order**; every configured validator must succeed. |
| `in_rate_limit` | int | `0` | Max inbound requests per caller ID, or client IP when no caller ID is available, within the window. |
| `out_rate_limit` | int | `0` | Max outbound requests per integration host within the window. |
| `rate_limit_window` | duration | `1m` | Window length for rate limiting. |
| `rate_limit_strategy` | string | `fixed_window` | Rate limit algorithm (`fixed_window`, `token_bucket`, or `leaky_bucket`). |
| `idle_conn_timeout` | duration | `90s` | How long idle connections stay pooled. |
| `tls_handshake_timeout` | duration | `10s` | Maximum time to wait for TLS handshakes. |
Expand Down Expand Up @@ -96,6 +96,8 @@ Two ways to authorise a caller:
# easiest: assign a capability
capabilities:
- name: post_as
params:
username: AuthTranslator

- id: service‑42
# granular example
Expand Down Expand Up @@ -150,7 +152,7 @@ authorised to use it.

## 3  `denylist.yaml` – request blockers

Denylists complement allowlists by describing requests that must never be forwarded. Each entry targets an integration and groups `CallRule` objects (the same schema as allowlist rules) per caller ID—just like the allowlist. Provide specific IDs for callers you want to block or use `"*"`/omit the field for a wildcard block. If **any** rule matches a request for that caller, the proxy immediately returns **403 Forbidden**.
Denylists describe requests that must never be forwarded. They are evaluated before allowlists, so a matching deny rule blocks the request even if an allowlist rule would otherwise permit it. Each entry targets an integration and groups `CallRule` objects (the same schema as allowlist rules) per caller ID. Provide specific IDs for callers you want to block or use `"*"`/omit the field for a wildcard block. If **any** rule matches a request for that caller, the proxy immediately returns **403 Forbidden**.

```yaml
- integration: example
Expand Down Expand Up @@ -215,4 +217,3 @@ CI fails fast on typos so you never ship an invalid proxy.
* [Auth Plugins](auth-plugins.md)
* [Secret Back-Ends](secret-backends.md)
* [Rate-Limiting](rate-limiting.md)

6 changes: 3 additions & 3 deletions docs/denylist-yaml.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ The **denylist** answers another narrow question:
It lives beside `config.yaml` and `allowlist.yaml`, is hot‑reloaded, and is entirely optional. In day‑to‑day operation you should
prefer shaping access through the [allowlist](allowlist-yaml.md); the denylist is best reserved for defensive blocks (for
example, temporarily disabling tool calls in the OpenAI Responses API or blocking a channel in Slack that should never be posted to). Both files can be used in tandem: a
request must first be permitted by the allowlist and then **avoid matching** any deny rules.
request must first **avoid matching** any deny rules and then be permitted by the allowlist.

---

Expand Down Expand Up @@ -71,9 +71,9 @@ strict:

While the denylist can stand alone, using both files together gives the best control:

1. The request must appear on the allowlist for the caller.
2. The proxy evaluates the denylist for that integration and caller. If any rule matches, the proxy responds with **403 Forbidden**
1. The proxy evaluates the denylist for that integration and caller. If any rule matches, the proxy responds with **403 Forbidden**
and sets `X-AT-Error-Reason` to the matching rule path.
2. If no deny rule matches, the request must appear on the allowlist for the caller.

Use the allowlist to declare the **intended** access surface, and keep the denylist as a precise safety net for exceptional cases
or incident response.
Expand Down
8 changes: 4 additions & 4 deletions docs/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ A lightweight reverse‑proxy that swaps the caller’s short‑lived credential

### 2 Does AuthTranslator ever store the caller’s credential?

No. Incoming plugins validate or inspect the token, derive a **caller ID**, then discard the credential before the request leaves the proxy.
No. Incoming plugins validate or inspect the credential, and plugins that implement caller identification derive a **caller ID**. Plugins that implement credential stripping remove the original credential before the request leaves the proxy.

---

Expand Down Expand Up @@ -51,7 +51,7 @@ Set `in_rate_limit: 0` and `out_rate_limit: 0` (or omit the fields entirely). Ra
* `env:` (environment variable)
* `file:` (volume‑mounted file)
* `gcp:` (Cloud KMS)
* `aws:` (AWS Secrets Manager)
* `aws:` (AES-GCM encrypted value using `AWS_KMS_KEY`)
* `azure:` (Azure Key Vault)
* `vault:` (HashiCorp Vault)

Expand Down Expand Up @@ -79,7 +79,7 @@ Yes. Both protocols are proxied transparently so long as the upstream service sp

### 11 Why am I seeing 429s even though I set `requests` high?

Remember limits are **per caller ID** *and* **per integration**. If your loadtest tool randomises caller IDs, each ID uses the limit configured in `config.yaml`. Consolidate IDs or raise the limit as needed.
Remember inbound limits are **per caller ID** and **per integration**. If your load-test tool randomises caller IDs, each ID uses the limit configured in `config.yaml`. Outbound limits are per integration host. Consolidate IDs, raise the limit, or adjust the strategy as needed.

---

Expand All @@ -104,4 +104,4 @@ Not built‑in. Most users expose Prometheus metrics to Grafana.

### 14 How do I log request/response headers for debugging?

Run the proxy with `-log-level DEBUG`. Secrets are automatically redacted.
The proxy does not dump request or response headers. Use WARN logs and `X-AT-Error-Reason` response headers to debug proxy-generated failures.
4 changes: 2 additions & 2 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Welcome to **AuthTranslator**! In a couple of minutes you’ll have a running pr
| ------------------------------------------ | ------------------------------------------------------------------------- |
| **Docker ≥ 24** | Easiest way to run the proxy without installing Go. |
| **Slack app token** (`SLACK_TOKEN`) | Long‑lived token with `chat:write` scope. |
| *(Optional)* **Go 1.24+** | Only needed if you’d like to run from source. |
| *(Optional)* **Go 1.26.2+** | Only needed if you’d like to run from source. |

> **Tip** A personal workspace app is fine for testing.

Expand All @@ -31,7 +31,7 @@ docker run --rm -p 8080:8080 \
`config.yaml` defines which integrations are available, `allowlist.yaml` controls which callers may use them, and
`denylist.yaml` lists requests that should always be rejected.

When the service starts it prints its version and log level to standard output.
The service validates the configuration during startup and exits before listening if it finds an error.

---

Expand Down
Loading
Loading