Skip to content
Merged
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
100 changes: 100 additions & 0 deletions datumctl/api-proxy.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
---
title: "Local API proxy"
sidebarTitle: "Local API proxy"
description: "Run a local authenticated proxy so any tool on your machine can talk to the Datum Cloud API without handling tokens."
---

`datumctl api proxy` starts a local HTTP proxy that forwards every request to the Datum Cloud API endpoint of your session, adding your credentials automatically and refreshing them as needed. Point any local tool — a dev server, a test harness, `curl` — at the printed URL and it can talk to the platform with no tokens to copy and no expiry to manage.

```bash
# Start a proxy on a fixed port, then call the API through it
datumctl api proxy --port 8001
curl http://127.0.0.1:8001/apis/resourcemanager.miloapis.com/v1alpha1/organizations
```

<Info>
Running in CI or an unattended pipeline? Use a [service account](/datumctl/auth/service-accounts) for the session the proxy serves — the proxy carries whatever identity you are logged in as.
</Info>

## Start the proxy

```bash
# Start a proxy on a fixed port for a dev server
datumctl api proxy --port 8001

# Start on a random free port; the URL is printed on the first stdout line
datumctl api proxy

# Serve a session other than your active one (see 'datumctl auth list' for names)
datumctl api proxy --session sam@datum.net@api.staging.env.datum.net

# Suppress per-request log lines
datumctl api proxy --quiet
```

By default the proxy picks a random free port, so starting a second proxy never fails. Two things are always printed once the listener is ready:

- A human-readable banner on **stderr** names the session, upstream, scope, and listen address, so you can verify at a glance whose credentials the proxy serves.
- The bare proxy URL (for example `http://127.0.0.1:52347`) is printed as the **first and only line on stdout**. Scripts and test harnesses can read that one line as their readiness signal.

```go
cmd := exec.Command("datumctl", "api", "proxy", "--quiet", "--project", testProject)
stdout, _ := cmd.StdoutPipe()
_ = cmd.Start()
apiURL, _ := bufio.NewReader(stdout).ReadString('\n') // first line = ready + address
```

Press `Ctrl+C` to stop the proxy. In-flight requests get a short grace period before their connections are closed.

## Paths and scoping

By default the proxy is a pure passthrough of the platform API surface: the same paths that work against the real API endpoint work against the local port, including the scoped organization and project control-plane prefixes. Swapping one base URL gives a tool dev/prod parity with nothing else to change.

Check warning on line 51 in datumctl/api-proxy.mdx

View check run for this annotation

Mintlify / Mintlify Validation (datum-4926dda5) - vale-spellcheck

datumctl/api-proxy.mdx#L51

Did you really mean 'passthrough'?

```bash
# Watch DNS zones on a project control plane through the proxy
curl "http://127.0.0.1:8001/apis/resourcemanager.miloapis.com/v1alpha1/projects/my-project/control-plane/apis/networking.datumapis.com/v1alpha/dnszones?watch=true"
```

With an explicit `--project` or `--organization` flag, the proxy instead serves that single control plane at its root, so URLs lose the long control-plane prefix:

```bash
datumctl api proxy --port 8001 --project my-project
curl "http://127.0.0.1:8001/apis/networking.datumapis.com/v1alpha/dnszones?watch=true"
```

<Note>
The session and scope are **pinned when the proxy starts** and shown in the banner. Switching your active account with [`datumctl auth switch`](/datumctl/auth/managing-accounts) or your context with `datumctl ctx use` does not affect a running proxy, and the proxy never inherits a scope from your current context — scoping is always an explicit flag. Restart the proxy to pick up a new session or scope.
</Note>

## Streaming

Streaming responses — watch requests, server-sent events, chunked transfer — pass through unbuffered: each event reaches your client the moment the upstream sends it, and the proxy never limits how long a response stays open. This makes the proxy suitable for long-lived watch clients.

Check warning on line 71 in datumctl/api-proxy.mdx

View check run for this annotation

Mintlify / Mintlify Validation (datum-4926dda5) - vale-spellcheck

datumctl/api-proxy.mdx#L71

Did you really mean 'unbuffered'?

## Credentials and token refresh

Every proxied request carries a fresh access token for the pinned session; the proxy refreshes the token before it expires and persists refreshed tokens exactly as other `datumctl` commands do. Service-account sessions work the same way.

Check warning on line 75 in datumctl/api-proxy.mdx

View check run for this annotation

Mintlify / Mintlify Validation (datum-4926dda5) - vale-spellcheck

datumctl/api-proxy.mdx#L75

Did you really mean 'proxied'?

If the session cannot be refreshed — expired, revoked, or logged out with `datumctl logout` — the proxy stays up and answers requests with a synthesized `502 Bad Gateway` carrying a JSON status body, an `X-Datum-Proxy-Error: true` marker header, and a message telling you to run `datumctl login`. The proxy recovers automatically, without a restart, once you log back in to the same session.

<Tip>
A `401` or `403` from the platform itself passes through unchanged and without the marker header, so your client can always tell a proxy-local authentication problem from a real platform answer.
</Tip>

## Security model

- **Loopback only.** The proxy listens on `127.0.0.1` and there is no flag to bind other addresses. Anything that should be reachable remotely deserves a tunnel whose security model you own.

Check warning on line 85 in datumctl/api-proxy.mdx

View check run for this annotation

Mintlify / Mintlify Validation (datum-4926dda5) - vale-spellcheck

datumctl/api-proxy.mdx#L85

Did you really mean 'Loopback'?
- **Local clients are trusted.** Like other local developer proxies, the proxy does not authenticate local clients: while it runs, any process on your machine can make API calls as the pinned session. The banner names the identity it serves, requests are logged by default, and the credential itself is never exposed — a local client can act through the proxy but cannot take your token with it.
- **Host-header validation.** Requests whose `Host` is not `localhost`, `127.0.0.1`, or `[::1]` are rejected with `403`, which defeats DNS-rebinding attacks from web pages.
- **No CORS headers.** Browsers refuse scripted cross-origin reads of proxy responses; the proxy is meant for server-side and command-line clients.
- **Authorization discipline.** Any `Authorization` header your local client sends is stripped and replaced with the session's real token, and tokens never appear in the request log.

## Request logging

One line per request is written to stderr — silence it with `--quiet`:

```
10:42:03 GET /apis/resourcemanager.miloapis.com/v1alpha1/organizations 200 143ms 8.1kB
10:42:05 GET /apis/…/projects/my-project/control-plane/…/portalplugins?watch=true 200 …streaming
```

Streaming responses log once when headers arrive (marked `…streaming`) and again when the stream ends, with total duration and bytes — so an abruptly closed watch is visible.
22 changes: 14 additions & 8 deletions docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,15 @@
"datumctl/resources/safe-changes"
]
},
{
"group": "Plugins",
"pages": [
"datumctl/plugins/using-plugins",
"datumctl/plugins/adding-catalogs",
"datumctl/plugins/publishing-catalogs",
"datumctl/plugins/building-plugins"
]
},
{
"group": "Activity & audit",
"pages": [
Expand All @@ -195,19 +204,16 @@
]
},
{
"group": "CI/CD",
"group": "Developer tooling",
"pages": [
"datumctl/cicd/github-actions",
"datumctl/cicd/automating"
"datumctl/api-proxy"
]
},
{
"group": "Plugins",
"group": "CI/CD",
"pages": [
"datumctl/plugins/using-plugins",
"datumctl/plugins/adding-catalogs",
"datumctl/plugins/publishing-catalogs",
"datumctl/plugins/building-plugins"
"datumctl/cicd/github-actions",
"datumctl/cicd/automating"
]
},
{
Expand Down