Repository navigation
Configuration Reference
Every value this project needs, where it lives, and what it should be set to.
There are four separate places configuration lives. They are not connected, and a value set in one never reaches another. Most setup problems are a value put in the wrong one.
| Store | Who reads it | When |
|---|---|---|
| GitHub → Actions secrets | The CI workflow | Only while deploying |
| Cloudflare → Worker secrets | The Worker itself | Every request, at runtime |
| ESP32 setup portal | The firmware | Every poll |
| Host command line | The agent (Linux or Windows) | Every push |
The single most common mistake: setting
MASTER_SECRET(orPUSH_TOKEN/READ_TOKEN) as a GitHub secret. Those are Cloudflare Worker secrets. A GitHub secret never reaches the running Worker. If requests come back saying a secret is not set, this is why.
| Name | Required | Value |
|---|---|---|
CLOUDFLARE_API_TOKEN |
Yes | Create at dash.cloudflare.com/profile/api-tokens → Create Token → use the Edit Cloudflare Workers template. Copy the token once; it is never shown again. |
CLOUDFLARE_ACCOUNT_ID |
Only if your token covers several accounts | Cloudflare dashboard → Workers & Pages → the Account ID in the right-hand sidebar. A 32-character hex string. |
Variables, not secrets. This one is not sensitive and needs to be readable in logs.
| Name | Required | Value |
|---|---|---|
WORKER_URL |
No, but the weekly health check silently does nothing without it | https://peek-relay.peekesp.workers.dev |
Base URL only — no path, no trailing slash needed. The workflow appends
/health itself. Putting /telemetry here makes the health check request
/telemetry/health, which 404s.
Add with Type: Secret (not Text). Plain text variables would work but stay readable in the dashboard forever.
| Name | Value |
|---|---|
MASTER_SECRET |
node cloudflare/mint.mjs --new-master |
That's all the Worker needs. Every tenant's tokens are derived from it:
push token = HMAC-SHA256(MASTER_SECRET, "<stream>:push") first 48 hex chars
read token = HMAC-SHA256(MASTER_SECRET, "<stream>:read") first 48 hex chars
Nothing is stored, so onboarding is offline — the deployment is never touched:
MASTER_SECRET=... npm run mint -- alice --url https://peek-relay.peekesp.workers.devIt prints that stream's two tokens plus the exact agent command and portal
settings to hand over. Their URLs become
…/ingest/alice and …/telemetry/alice, and their data lives in its own
Durable Object — invisible to every other stream.
MASTER_SECRETis the crown jewel. Anyone holding it can mint tokens for any stream, including read tokens for streams that aren't theirs. It belongs inwrangler secret putand on whichever machine you mint from — nowhere else, and never in GitHub.
Revoking one tenant: tokens are a pure function of (master secret, stream),
so you can't revoke one in place. Move them to a new stream name — alice →
alice-v2 — which changes both their tokens and leaves everyone else alone.
Rotating MASTER_SECRET invalidates every stream at once.
| Name | Value |
|---|---|
PUSH_TOKEN |
A fresh random string — openssl rand -hex 24
|
READ_TOKEN |
A different random string — run the command again |
Use hex, not base64: the Worker accepts a ?token= fallback and base64's
+ / = would need URL-escaping there. Keep them ≤ 64 characters — the firmware
stores the read token in a 65-byte buffer. -hex 24 gives 48, with margin;
-hex 32 gives exactly 64 and leaves none.
They must differ. The Worker rejects each token on the other's endpoint, which is the entire point: the ESP32 sits on a desk carrying the read token in flash, so if the device is taken apart, that token still cannot push fabricated telemetry.
These use the un-suffixed URLs, /ingest and /telemetry. Private is not a
deprecated path — it is the right choice when the relay is only ever yours.
Both modes can be active on the same deployment at once.
Secrets take effect immediately — no redeploy. wrangler deploy deliberately
never touches them, which is why you set them once and they survive every
future deploy.
Hold the left button 1.5 s (or power on holding it), scan the QR code, then
browse to 192.168.4.1.
| Field | Value |
|---|---|
| Network (SSID) | Your WiFi — pick from the dropdown |
| Password | Your WiFi passphrase |
| Worker URL |
…/telemetry (private) or …/telemetry/<stream> (shared) |
| Read token | The read token — never the push one |
| Verify TLS certificate | Checked |
| Poll seconds | 5 |
If you paired from the app these are already filled in and there is nothing to change here.
Windows uses windows/peek-agent-win.py (or the compiled
peek-agent.exe); flags and JSON are identical to the Linux agent. It reports
cpu_temp_c: -1, which the display renders as --, because Windows exposes no
temperature without a vendor driver.
python3 peek-agent.py --push https://peek-relay.peekesp.workers.dev/ingest --token PUSH_TOKEN_HEREShared mode — note the stream name on the end:
python3 peek-agent.py --push https://peek-relay.peekesp.workers.dev/ingest/alice --token ALICE_PUSH_TOKENOr keep the token out of your shell history:
export PEEK_PUSH_TOKEN=...
python3 peek-agent.py --push https://peek-relay.peekesp.workers.dev/ingestAdd --no-serve if you do not also want the local :8080 endpoint.
This trips people up, because all three look like "the Worker URL":
| Used by | URL |
|---|---|
GitHub WORKER_URL variable |
https://peek-relay.peekesp.workers.dev |
| ESP32 setup portal |
…/telemetry or …/telemetry/<stream>
|
Host agent --push
|
…/ingest or …/ingest/<stream>
|
A stream's two URLs must use the same stream name, or the device reads a
slot nothing is writing to and sits at NO LINK.