From 36e8149cde728ac68b81ddd0d51163f6815b96eb Mon Sep 17 00:00:00 2001 From: Rae McKelvey <633012+okdistribute@users.noreply.github.com> Date: Tue, 11 Aug 2026 15:48:59 -0700 Subject: [PATCH 1/3] docs: explain that API keys are project-wide secrets The API keys page said only "load the key from a config file or environment variable instead of hardcoding it", which doesn't tell people what the actual risk is or what to do when their app ships to devices they don't control. Spell out that an API key is a long-lived, unscoped, project-wide bearer secret, that it's extractable from any binary it ships in, and that the relay token minting doesn't help because the token is minted locally from the key. Then cover where the key does belong: endpoints you operate, including MDM-managed fleets where it arrives as a managed configuration and can be revoked centrally. Per-user authorization is a layer you add with endpoint hooks. Note that per-endpoint access issued through your own application server is in progress and explicitly not implemented yet. Adds an FAQ entry for this, taken from a real user question. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01NQRfUmiEunzASEtgGhJ872 --- about/faq.mdx | 35 ++++++++++++++++++ iroh-services/access.mdx | 80 +++++++++++++++++++++++++++++++++++++--- 2 files changed, 109 insertions(+), 6 deletions(-) diff --git a/about/faq.mdx b/about/faq.mdx index 56829a1..4ed606f 100644 --- a/about/faq.mdx +++ b/about/faq.mdx @@ -113,6 +113,41 @@ Iroh gives you full control over which endpoints are allowed to connect via [end That controls who your application accepts. If you run your own relay, you can separately control which endpoints the relay itself will carry traffic for, by endpoint ID, a shared token, or a callback to your own service. That is configured on the relay itself; see the [relay documentation](https://github.com/n0-computer/iroh/tree/main/iroh-relay). +## How do I use Iroh Services in an app I ship to users? I can't put my API key on their devices. + +You're right that you can't, and the good news is that you don't have to: **iroh +connections never require an API key.** Two endpoints connecting to each other +and NAT traversal work with no credentials at all. An +[API key](/iroh-services/access) is only needed for pushing metrics to your +project, sending net diagnostics reports, and connecting to an authenticated +[dedicated relay](/iroh-services/relays/managed). + +Your instinct about shipping it is correct. An API key is a long-lived, +project-wide bearer secret with no scopes, so a copy extracted from your binary +grants full access to your project until you rotate it. + +So split it in two: + +- **The key stays on endpoints you operate.** The test is whether you control who + can read the machine and whether you can rotate the key without shipping a new + release. Your own backend services pass it, and so do MDM-managed fleets, where + the key arrives as a managed configuration and can be revoked from the console — + which makes enterprise deployments a good fit for the full feature set. Client + endpoints on devices you don't administer run with no key and connect normally. +- **Access control for your users is a layer you add**, using [endpoint + hooks](/connecting/endpoint-hooks). A hook intercepts an incoming connection + before it's accepted, so you decide who may connect based on the endpoint's ID, + an allowlist your backend maintains, a token your app issues, or any policy you + like. That's per-user authorization living in your code, backed by your own + authentication. + +We're building per-endpoint access issued by your own application server, where +your backend authenticates a user and hands their endpoint a scoped credential +without the API key ever reaching the device. It isn't implemented yet, so for +now that layer is yours to build. See [API Keys](/iroh-services/access) for the +full picture. + + ## What is "Address Lookup" in iroh and which one should I enable? For most usage, using the services that are enabled with the `iroh::endpoint::presets::N0` preset is the best default. diff --git a/iroh-services/access.mdx b/iroh-services/access.mdx index 045169e..3ba40ad 100644 --- a/iroh-services/access.mdx +++ b/iroh-services/access.mdx @@ -51,15 +51,83 @@ let client = preset.client_builder(&endpoint).build().await?; The preset carries the key, so you don't pass it to the client a second time. -In production, load the key from a config file or environment variable instead -of hardcoding it. +The literal string above is for illustration. Load the key from an environment +variable or a secret manager instead, using `api_secret_from_env()` to read +`IROH_SERVICES_API_SECRET`. For a full walkthrough (creating an endpoint, naming it, and verifying it on the dashboard) see the [Iroh Services quickstart](/iroh-services/quickstart). +## An API key is a project-wide secret + +This is the most important thing to understand about API keys, and the place +people most often get it wrong. + +An API key is a long-lived shared secret that carries the authority of your +whole project. It has no per-endpoint identity and no scopes: **any** process +holding it can push metrics, send diagnostics reports, and connect to your +dedicated relays as you. It's a bearer credential; whoever has the bytes has the +access. + +That is fine wherever you control the machine and can load the key from the +environment or a secret manager, out of reach of anyone else. + +It is **not** safe to ship inside an application that runs on someone else's +device. A key compiled into a binary, bundled into a mobile app, or dropped into +a desktop installer is extractable. `strings` on the binary, a decompiled APK, or +a proxy on the device will all find it, and obfuscation only changes how long +that takes. Once it's out, it works from anywhere until you rotate it — and +rotating it breaks every copy of your app that's already installed. + + +The [token minting](/concepts/relays#authentication) that happens when your +endpoint connects to a dedicated relay does not change this. The token is +short-lived and scoped to that endpoint, but it is minted *locally from the API +key*, so the key still has to be present on the machine. It protects the key in +transit, not on disk. + + +## What to do instead + +API keys are a building block for your own authentication system, not a +replacement for one. If your app runs on machines you don't control, you decide +who gets access, and the key stays on your side. + +**Keep the key on endpoints you operate.** The test is whether you control who +can read the machine's disk and process environment, and whether you can rotate +the key on it without shipping a new release. If both are yes, the key belongs +there. + +That covers the backend services in your own architecture — the endpoint that +receives uploads, the coordinator your clients dial, the worker that syncs +between regions — and it also covers managed device fleets. On an enterprise +deployment where machines are enrolled in MDM, the key is delivered as a managed +configuration or platform secret rather than compiled into the app, and you can +rotate or revoke it across the fleet from the console. Those endpoints are +infrastructure you operate in every sense that matters here, so metrics, net +diagnostics, and authenticated dedicated relays all work as intended. + +What's left over is a consumer application on a device you don't administer. +There the key has nowhere safe to live, so it doesn't go there at all: those +endpoints run with no key and connect to your services and to each other +normally, because iroh connections themselves never require one. + +**Add your own authorization layer with endpoint hooks.** Deciding which users +may do what is your application's job, and [endpoint +hooks](/connecting/endpoint-hooks) are where that decision goes. A hook +intercepts an incoming connection before it's accepted, so you can allow or +reject it based on the connecting endpoint's ID, an allowlist your backend +maintains, a token your app issues, or any policy you like. That gives you +per-user access control that lives in your code, backed by your own +authentication, with the API key never leaving your infrastructure. + ## What's next -Today, API keys gate metrics and net diagnostics uploads and access to your -dedicated relays. Future versions may support per-resource permissions and -time-based access control. If your use case needs that today, [contact -us](https://n0.computer/contact). +We're working on per-endpoint access issued through your own application server: +your backend authenticates a user, then hands that user's endpoint a credential +carrying the scopes you chose for it, without your API key ever touching the +device. **This is not implemented yet.** Until it ships, the patterns above are +the way to do it, and the work of deciding who gets what is yours. + +If your use case needs this, [contact us](https://n0.computer/contact) — we want +to hear about the shape of it. From 3cecf724ab6971e3d1872a667654df0a6334cf2e Mon Sep 17 00:00:00 2001 From: rae <633012+okdistribute@users.noreply.github.com> Date: Tue, 11 Aug 2026 16:28:36 -0700 Subject: [PATCH 2/3] Update faq.mdx --- about/faq.mdx | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/about/faq.mdx b/about/faq.mdx index 4ed606f..50628f6 100644 --- a/about/faq.mdx +++ b/about/faq.mdx @@ -120,32 +120,23 @@ connections never require an API key.** Two endpoints connecting to each other and NAT traversal work with no credentials at all. An [API key](/iroh-services/access) is only needed for pushing metrics to your project, sending net diagnostics reports, and connecting to an authenticated -[dedicated relay](/iroh-services/relays/managed). +[relay](/concepts/relays#authentication). Your instinct about shipping it is correct. An API key is a long-lived, project-wide bearer secret with no scopes, so a copy extracted from your binary -grants full access to your project until you rotate it. - -So split it in two: - -- **The key stays on endpoints you operate.** The test is whether you control who - can read the machine and whether you can rotate the key without shipping a new - release. Your own backend services pass it, and so do MDM-managed fleets, where - the key arrives as a managed configuration and can be revoked from the console — - which makes enterprise deployments a good fit for the full feature set. Client - endpoints on devices you don't administer run with no key and connect normally. -- **Access control for your users is a layer you add**, using [endpoint - hooks](/connecting/endpoint-hooks). A hook intercepts an incoming connection - before it's accepted, so you decide who may connect based on the endpoint's ID, - an allowlist your backend maintains, a token your app issues, or any policy you - like. That's per-user authorization living in your code, backed by your own - authentication. - -We're building per-endpoint access issued by your own application server, where +grants access to send metrics and connect to your relays until you rotate it. + +- **Enterprise deployments can use API keys out of the box.* If you control who can read the machine and whether you can rotate the key without shipping a new + release, then you are ok. Run this on an MDM-managed fleet, in servers, or in CI, where + the key arrives as a managed configuration and can be revoked from a console. +- **For consumer apps, we recommend adding an authentication layer.** Build an authentication layer to decide who may connect based on the endpoint's ID, + with an allowlist your backend maintains, a token your app issues, or any policy you + like. You control per-user authorization living in your code, on your servers -- iroh is unopinionated about this. + +We are unhappy with this story currently so we are building per-endpoint access issued by your own application server, where your backend authenticates a user and hands their endpoint a scoped credential without the API key ever reaching the device. It isn't implemented yet, so for -now that layer is yours to build. See [API Keys](/iroh-services/access) for the -full picture. +now that layer is yours to build until that feature ships. ## What is "Address Lookup" in iroh and which one should I enable? From c194228d8f756c9ade5932f71a131948e42e3a0f Mon Sep 17 00:00:00 2001 From: rae <633012+okdistribute@users.noreply.github.com> Date: Tue, 11 Aug 2026 16:43:29 -0700 Subject: [PATCH 3/3] Update access.mdx --- iroh-services/access.mdx | 48 ++++------------------------------------ 1 file changed, 4 insertions(+), 44 deletions(-) diff --git a/iroh-services/access.mdx b/iroh-services/access.mdx index 3ba40ad..3249142 100644 --- a/iroh-services/access.mdx +++ b/iroh-services/access.mdx @@ -60,66 +60,26 @@ dashboard) see the [Iroh Services quickstart](/iroh-services/quickstart). ## An API key is a project-wide secret -This is the most important thing to understand about API keys, and the place -people most often get it wrong. - An API key is a long-lived shared secret that carries the authority of your whole project. It has no per-endpoint identity and no scopes: **any** process holding it can push metrics, send diagnostics reports, and connect to your dedicated relays as you. It's a bearer credential; whoever has the bytes has the access. -That is fine wherever you control the machine and can load the key from the -environment or a secret manager, out of reach of anyone else. - -It is **not** safe to ship inside an application that runs on someone else's -device. A key compiled into a binary, bundled into a mobile app, or dropped into -a desktop installer is extractable. `strings` on the binary, a decompiled APK, or -a proxy on the device will all find it, and obfuscation only changes how long -that takes. Once it's out, it works from anywhere until you rotate it — and -rotating it breaks every copy of your app that's already installed. - - -The [token minting](/concepts/relays#authentication) that happens when your -endpoint connects to a dedicated relay does not change this. The token is -short-lived and scoped to that endpoint, but it is minted *locally from the API -key*, so the key still has to be present on the machine. It protects the key in -transit, not on disk. - - -## What to do instead - API keys are a building block for your own authentication system, not a replacement for one. If your app runs on machines you don't control, you decide who gets access, and the key stays on your side. -**Keep the key on endpoints you operate.** The test is whether you control who -can read the machine's disk and process environment, and whether you can rotate -the key on it without shipping a new release. If both are yes, the key belongs -there. +**Keep the key on endpoints you operate.** If you can control the machine's disk and process environment, and whether you can rotate +the key on it without shipping a new release. If both are yes, the key is recoverable. -That covers the backend services in your own architecture — the endpoint that -receives uploads, the coordinator your clients dial, the worker that syncs -between regions — and it also covers managed device fleets. On an enterprise -deployment where machines are enrolled in MDM, the key is delivered as a managed +On an enterprise deployment where machines are enrolled in an MDM, the key is delivered as a managed configuration or platform secret rather than compiled into the app, and you can rotate or revoke it across the fleet from the console. Those endpoints are infrastructure you operate in every sense that matters here, so metrics, net diagnostics, and authenticated dedicated relays all work as intended. -What's left over is a consumer application on a device you don't administer. -There the key has nowhere safe to live, so it doesn't go there at all: those -endpoints run with no key and connect to your services and to each other -normally, because iroh connections themselves never require one. - -**Add your own authorization layer with endpoint hooks.** Deciding which users -may do what is your application's job, and [endpoint -hooks](/connecting/endpoint-hooks) are where that decision goes. A hook -intercepts an incoming connection before it's accepted, so you can allow or -reject it based on the connecting endpoint's ID, an allowlist your backend -maintains, a token your app issues, or any policy you like. That gives you -per-user access control that lives in your code, backed by your own -authentication, with the API key never leaving your infrastructure. +What doesn't work so well is a consumer application on a device you don't administer. In those cases, you'll need to build your own authentication layer that allows you to control authentication with your own token, service, or otherwise. On it's own, iroh is not opinionated about authentication so you can use whatever system you want, forever. ## What's next