Project tokens and rotation
A project credential is a project id and a public token, sent as HTTP Basic on every request. Why it can be public, and what it allows: Project credentials. This page is the procedures.
Make a token and its registry line
Section titled “Make a token and its registry line”echo "pt_$(openssl rand -hex 16)"The collector reads projects from an htpasswd file, one line per project: <project id>:{SHA}<base64 of the SHA-1 of the token>. With htpasswd (package apache2-utils):
htpasswd -nbs web-store pt_9f3c…Without it, the same line with openssl:
printf '%s:{SHA}%s\n' web-store "$(printf '%s' pt_9f3c… | openssl dgst -binary -sha1 | openssl base64)"Use {SHA} or apr1, never bcrypt: the hash runs on every request. Project ids must match ^[a-z0-9][a-z0-9._-]{0,63}$.
Add a project
Section titled “Add a project”Append the line to projects.htpasswd (Deploy with Docker Compose), then restart the collector. It reads the file once, at start:
docker compose restart otel-collectorWith your own Secret (collector.existingProjectsSecret), add the line to it:
kubectl -n ouo create secret generic ouo-projects --from-file=projects.htpasswd=./projects.htpasswd --dry-run=client -o yaml | kubectl apply -f -Then restart the collector, because a Secret you manage outside the chart does not change the chart’s checksum:
kubectl -n ouo rollout restart deploy/ouo-collectorWith projects in your values file instead, add an entry and run helm upgrade ouo ./ouo -n ouo -f my-values.yaml: the chart renders the Secret, and its checksum rolls the collector.
Check it: 401 before the restart, 200 after.
curl -s -o /dev/null -w '%{http_code}\n' -X POST https://otel.example.com/v1/traces -u "web-store:pt_9f3c…" -H 'Content-Type: application/json' -d '{"resourceSpans":[]}'Give an app its credential
Section titled “Give an app its credential”initRUM({ endpoint: "https://otel.example.com", projectId: "web-store", projectToken: "pt_9f3c…", service: { name: "web-store", version: "1.4.2", environment: "production" },});Any OpenTelemetry SDK:
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Basic%20$(printf 'web-store:pt_9f3c…' | base64)"%20 is how the OTLP environment variables write the space after Basic.
Rotate a token
Section titled “Rotate a token”- 1 new token, new build
- 2 replace line, restart
- 3 Basic …:pt_old
- 4 401 · invalid_credentials
- 5 Basic …:pt_new
- 6 200
-
Make the new token and build your app with it, but do not release it yet.
-
Replace the project’s line in the htpasswd file with one for the new token, and restart the collector as in Add a project.
-
Release the app. Web apps switch on the next page load. Watch the old token fade out:
sum by (reason) (rate(ouo_auth_failures_total[5m]))
For mobile apps, a cut-over cuts off every installed version that has not updated. Rotate to a new project id instead: add mobile-store-2 with its own token next to mobile-store, ship the new app version with it, and delete the old line once the old versions are gone. Both ids are valid at once. In Grafana, filter on both projects meanwhile.
Revoke a token
Section titled “Revoke a token”Delete the project’s line and restart the collector. Every request with that credential gets 401 from then on. Data already stored stays; to delete it too, use rum-api’s admin API (rum-api).
Rotate the server-side secrets
Section titled “Rotate the server-side secrets”These are never in a client, so rotating them is a restart, not a release.
| Secret | Set the new value on | Then |
|---|---|---|
| ingest token | the collector and rum-api (OUO_INGEST_TOKEN; Helm: ingest-token in the rum-api Secret) |
restart both. The collector retries chunks that rum-api refused in between for up to 5 minutes |
| read token | rum-api and Grafana (OUO_READ_TOKEN, provisioned into the app’s secure settings; Helm: read-token) |
restart both. Or change it on the app’s configuration page in Grafana, /plugins/ouo11y-rum-app, without a Grafana restart |
| admin token | rum-api (OUO_ADMIN_TOKEN; Helm: admin-token) |
restart rum-api |
internal project token |
the htpasswd file and rum-api’s OTEL_EXPORTER_OTLP_HEADERS (OUO_BACKEND_OTLP_HEADERS; Helm: otlp-headers) |
restart both, or rum-api’s own telemetry is refused with 401 |
In Docker Compose, docker compose up -d after editing .env recreates exactly the containers whose variables changed. In Helm, a change to a chart-rendered Secret rolls the pods that use it; for a Secret you manage, run kubectl rollout restart.